@mailshot/cdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/bin/app.d.ts +3 -0
  2. package/dist/bin/app.d.ts.map +1 -0
  3. package/dist/bin/app.js +94 -0
  4. package/dist/bin/app.js.map +1 -0
  5. package/dist/lib/constructs/event-bus.d.ts +16 -0
  6. package/dist/lib/constructs/event-bus.d.ts.map +1 -0
  7. package/dist/lib/constructs/event-bus.js +119 -0
  8. package/dist/lib/constructs/event-bus.js.map +1 -0
  9. package/dist/lib/constructs/lambdas.d.ts +26 -0
  10. package/dist/lib/constructs/lambdas.d.ts.map +1 -0
  11. package/dist/lib/constructs/lambdas.js +178 -0
  12. package/dist/lib/constructs/lambdas.js.map +1 -0
  13. package/dist/lib/constructs/ses-config.d.ts +14 -0
  14. package/dist/lib/constructs/ses-config.d.ts.map +1 -0
  15. package/dist/lib/constructs/ses-config.js +75 -0
  16. package/dist/lib/constructs/ses-config.js.map +1 -0
  17. package/dist/lib/constructs/ssm-params.d.ts +17 -0
  18. package/dist/lib/constructs/ssm-params.d.ts.map +1 -0
  19. package/dist/lib/constructs/ssm-params.js +63 -0
  20. package/dist/lib/constructs/ssm-params.js.map +1 -0
  21. package/dist/lib/constructs/state-machines.d.ts +21 -0
  22. package/dist/lib/constructs/state-machines.d.ts.map +1 -0
  23. package/dist/lib/constructs/state-machines.js +216 -0
  24. package/dist/lib/constructs/state-machines.js.map +1 -0
  25. package/dist/lib/constructs/storage.d.ts +17 -0
  26. package/dist/lib/constructs/storage.d.ts.map +1 -0
  27. package/dist/lib/constructs/storage.js +91 -0
  28. package/dist/lib/constructs/storage.js.map +1 -0
  29. package/dist/lib/load-sequences.d.ts +7 -0
  30. package/dist/lib/load-sequences.d.ts.map +1 -0
  31. package/dist/lib/load-sequences.js +76 -0
  32. package/dist/lib/load-sequences.js.map +1 -0
  33. package/dist/lib/mailshot-stack.d.ts +13 -0
  34. package/dist/lib/mailshot-stack.d.ts.map +1 -0
  35. package/dist/lib/mailshot-stack.js +155 -0
  36. package/dist/lib/mailshot-stack.js.map +1 -0
  37. package/dist/lib/step-func-emailer-stack.d.ts +13 -0
  38. package/dist/lib/step-func-emailer-stack.d.ts.map +1 -0
  39. package/dist/lib/step-func-emailer-stack.js +155 -0
  40. package/dist/lib/step-func-emailer-stack.js.map +1 -0
  41. package/package.json +55 -0
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import "source-map-support/register";
3
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../bin/app.ts"],"names":[],"mappings":";AACA,OAAO,6BAA6B,CAAC"}
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ require("source-map-support/register");
38
+ const path = __importStar(require("node:path"));
39
+ const fs = __importStar(require("node:fs"));
40
+ const cdk = __importStar(require("aws-cdk-lib"));
41
+ const mailshot_stack_js_1 = require("../lib/mailshot-stack.js");
42
+ const load_sequences_js_1 = require("../lib/load-sequences.js");
43
+ // Load .env from repo root
44
+ const envPath = path.join(__dirname, "../../../.env");
45
+ if (fs.existsSync(envPath)) {
46
+ for (const line of fs.readFileSync(envPath, "utf-8").split("\n")) {
47
+ const trimmed = line.trim();
48
+ if (!trimmed || trimmed.startsWith("#"))
49
+ continue;
50
+ const idx = trimmed.indexOf("=");
51
+ if (idx === -1)
52
+ continue;
53
+ const key = trimmed.slice(0, idx);
54
+ const value = trimmed.slice(idx + 1);
55
+ if (!(key in process.env)) {
56
+ process.env[key] = value;
57
+ }
58
+ }
59
+ }
60
+ function required(name) {
61
+ const val = process.env[name];
62
+ if (!val) {
63
+ throw new Error(`Missing environment variable ${name}. Copy .env.example to .env and fill in your values.`);
64
+ }
65
+ return val;
66
+ }
67
+ const config = {
68
+ account: required("ACCOUNT"),
69
+ region: required("REGION"),
70
+ stackName: required("STACK_NAME"),
71
+ tableName: required("TABLE_NAME"),
72
+ eventsTableName: required("EVENTS_TABLE_NAME"),
73
+ templateBucketName: required("TEMPLATE_BUCKET_NAME"),
74
+ eventBusName: required("EVENT_BUS_NAME"),
75
+ sesConfigSetName: required("SES_CONFIG_SET_NAME"),
76
+ snsTopicName: required("SNS_TOPIC_NAME"),
77
+ defaultFromEmail: required("DEFAULT_FROM_EMAIL"),
78
+ defaultFromName: required("DEFAULT_FROM_NAME"),
79
+ replyToEmail: process.env.REPLY_TO_EMAIL || undefined,
80
+ unsubscribeSecret: required("UNSUBSCRIBE_SECRET"),
81
+ ssmPrefix: required("SSM_PREFIX"),
82
+ };
83
+ const definitions = (0, load_sequences_js_1.loadSequenceConfigs)();
84
+ const app = new cdk.App();
85
+ new mailshot_stack_js_1.MailshotStack(app, config.stackName, {
86
+ env: {
87
+ account: config.account,
88
+ region: config.region,
89
+ },
90
+ config,
91
+ definitions,
92
+ templateBuildDir: path.resolve(__dirname, "../../build"),
93
+ });
94
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../bin/app.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uCAAqC;AACrC,gDAAkC;AAClC,4CAA8B;AAC9B,iDAAmC;AAEnC,gEAAyD;AACzD,gEAA+D;AAE/D,2BAA2B;AAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AACtD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,SAAS;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,sDAAsD,CAC3F,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,MAAM,GAAmB;IAC7B,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC;IAC5B,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAC1B,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC;IACjC,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC;IACjC,eAAe,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IAC9C,kBAAkB,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IACpD,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IACxC,gBAAgB,EAAE,QAAQ,CAAC,qBAAqB,CAAC;IACjD,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IACxC,gBAAgB,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IAChD,eAAe,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IAC9C,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,SAAS;IACrD,iBAAiB,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACjD,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC;CAClC,CAAC;AAEF,MAAM,WAAW,GAAG,IAAA,uCAAmB,GAAE,CAAC;AAE1C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;AAE1B,IAAI,iCAAa,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE;IACvC,GAAG,EAAE;QACH,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB;IACD,MAAM;IACN,WAAW;IACX,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC;CACzD,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import * as events from "aws-cdk-lib/aws-events";
2
+ import type * as sfn from "aws-cdk-lib/aws-stepfunctions";
3
+ import type * as lambda from "aws-cdk-lib/aws-lambda";
4
+ import { Construct } from "constructs";
5
+ import type { SequenceDefinition } from "@mailshot/shared";
6
+ export interface EventBusProps {
7
+ eventBusName: string;
8
+ definitions: SequenceDefinition[];
9
+ stateMachines: Map<string, sfn.StateMachine>;
10
+ sendEmailFn: lambda.IFunction;
11
+ }
12
+ export declare class EventBusConstruct extends Construct {
13
+ readonly eventBus: events.EventBus;
14
+ constructor(scope: Construct, id: string, props: EventBusProps);
15
+ }
16
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../../lib/constructs/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,KAAK,GAAG,MAAM,+BAA+B,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC;CAC/B;AASD,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,SAAgB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;gBAE9B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa;CA8E/D"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EventBusConstruct = void 0;
37
+ const events = __importStar(require("aws-cdk-lib/aws-events"));
38
+ const targets = __importStar(require("aws-cdk-lib/aws-events-targets"));
39
+ const constructs_1 = require("constructs");
40
+ function pascalCase(id) {
41
+ return id
42
+ .split(/[-_]/)
43
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
44
+ .join("");
45
+ }
46
+ class EventBusConstruct extends constructs_1.Construct {
47
+ eventBus;
48
+ constructor(scope, id, props) {
49
+ super(scope, id);
50
+ this.eventBus = new events.EventBus(this, "Bus", {
51
+ eventBusName: props.eventBusName,
52
+ });
53
+ for (const def of props.definitions) {
54
+ const prefix = pascalCase(def.id);
55
+ // ── Sequence trigger → Step Function ─────────────────────────────
56
+ const sm = props.stateMachines.get(def.id);
57
+ if (!sm) {
58
+ throw new Error(`No state machine found for sequence '${def.id}'`);
59
+ }
60
+ const mapping = def.trigger.subscriberMapping;
61
+ const subscriberInput = {
62
+ email: events.EventField.fromPath(mapping.email),
63
+ firstName: events.EventField.fromPath(mapping.firstName),
64
+ };
65
+ if (mapping.attributes) {
66
+ subscriberInput.attributes = events.EventField.fromPath(mapping.attributes);
67
+ }
68
+ const ruleSlug = def.trigger.detailType.replace(/[^a-zA-Z0-9]/g, "-");
69
+ new events.Rule(this, `${prefix}Rule`, {
70
+ eventBus: this.eventBus,
71
+ ruleName: `${def.id}-${ruleSlug}`,
72
+ eventPattern: {
73
+ detailType: [def.trigger.detailType],
74
+ },
75
+ targets: [
76
+ new targets.SfnStateMachine(sm, {
77
+ input: events.RuleTargetInput.fromObject({
78
+ subscriber: subscriberInput,
79
+ }),
80
+ }),
81
+ ],
82
+ });
83
+ // ── Event-triggered fire-and-forget emails ───────────────────────
84
+ if (def.events) {
85
+ for (let i = 0; i < def.events.length; i++) {
86
+ const evt = def.events[i];
87
+ const evtSlug = evt.detailType.replace(/[^a-zA-Z0-9]/g, "-");
88
+ const evtMapping = evt.subscriberMapping ?? def.trigger.subscriberMapping;
89
+ const evtSubscriber = {
90
+ email: events.EventField.fromPath(evtMapping.email),
91
+ firstName: events.EventField.fromPath(evtMapping.firstName),
92
+ };
93
+ if (evtMapping.attributes) {
94
+ evtSubscriber.attributes = events.EventField.fromPath(evtMapping.attributes);
95
+ }
96
+ new events.Rule(this, `${prefix}Event${i + 1}Rule`, {
97
+ eventBus: this.eventBus,
98
+ ruleName: `${def.id}-${evtSlug}`,
99
+ eventPattern: {
100
+ detailType: [evt.detailType],
101
+ },
102
+ targets: [
103
+ new targets.LambdaFunction(props.sendEmailFn, {
104
+ event: events.RuleTargetInput.fromObject({
105
+ action: "fire_and_forget",
106
+ templateKey: evt.templateKey,
107
+ subject: evt.subject,
108
+ subscriber: evtSubscriber,
109
+ }),
110
+ }),
111
+ ],
112
+ });
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ exports.EventBusConstruct = EventBusConstruct;
119
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../../lib/constructs/event-bus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";
2
+ import type * as dynamodb from "aws-cdk-lib/aws-dynamodb";
3
+ import type * as s3 from "aws-cdk-lib/aws-s3";
4
+ import type * as sns from "aws-cdk-lib/aws-sns";
5
+ import { Construct } from "constructs";
6
+ export interface LambdasProps {
7
+ table: dynamodb.Table;
8
+ eventsTable: dynamodb.Table;
9
+ templateBucket: s3.Bucket;
10
+ ssmPrefix: string;
11
+ snsTopic: sns.Topic;
12
+ sesConfigSetName: string;
13
+ logLevel?: string;
14
+ handlersPath?: string;
15
+ }
16
+ export declare class LambdasConstruct extends Construct {
17
+ readonly sendEmailFn: nodejs.NodejsFunction;
18
+ readonly checkConditionFn: nodejs.NodejsFunction;
19
+ readonly unsubscribeFn: nodejs.NodejsFunction;
20
+ readonly bounceHandlerFn: nodejs.NodejsFunction;
21
+ readonly engagementHandlerFn: nodejs.NodejsFunction;
22
+ readonly unsubscribeFnUrl: string;
23
+ constructor(scope: Construct, id: string, props: LambdasProps);
24
+ private grantSsmRead;
25
+ }
26
+ //# sourceMappingURL=lambdas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambdas.d.ts","sourceRoot":"","sources":["../../../lib/constructs/lambdas.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AACxD,OAAO,KAAK,KAAK,QAAQ,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;IACtB,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC;IAC5B,cAAc,EAAE,EAAE,CAAC,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,SAAgB,WAAW,EAAE,MAAM,CAAC,cAAc,CAAC;IACnD,SAAgB,gBAAgB,EAAE,MAAM,CAAC,cAAc,CAAC;IACxD,SAAgB,aAAa,EAAE,MAAM,CAAC,cAAc,CAAC;IACrD,SAAgB,eAAe,EAAE,MAAM,CAAC,cAAc,CAAC;IACvD,SAAgB,mBAAmB,EAAE,MAAM,CAAC,cAAc,CAAC;IAC3D,SAAgB,gBAAgB,EAAE,MAAM,CAAC;gBAE7B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY;IAuI7D,OAAO,CAAC,YAAY;CAiBrB"}
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LambdasConstruct = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const cdk = __importStar(require("aws-cdk-lib"));
39
+ const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
40
+ const nodejs = __importStar(require("aws-cdk-lib/aws-lambda-nodejs"));
41
+ const snsSubscriptions = __importStar(require("aws-cdk-lib/aws-sns-subscriptions"));
42
+ const iam = __importStar(require("aws-cdk-lib/aws-iam"));
43
+ const constructs_1 = require("constructs");
44
+ class LambdasConstruct extends constructs_1.Construct {
45
+ sendEmailFn;
46
+ checkConditionFn;
47
+ unsubscribeFn;
48
+ bounceHandlerFn;
49
+ engagementHandlerFn;
50
+ unsubscribeFnUrl;
51
+ constructor(scope, id, props) {
52
+ super(scope, id);
53
+ function resolveHandlersPath() {
54
+ const entry = require.resolve("@mailshot/handlers");
55
+ return path.join(path.dirname(entry), "../src");
56
+ }
57
+ const handlersPath = props.handlersPath ?? resolveHandlersPath();
58
+ const commonBundling = {
59
+ minify: true,
60
+ sourceMap: true,
61
+ target: "node22",
62
+ format: nodejs.OutputFormat.CJS,
63
+ externalModules: [
64
+ "@aws-sdk/client-dynamodb",
65
+ "@aws-sdk/client-s3",
66
+ "@aws-sdk/client-sesv2",
67
+ "@aws-sdk/client-sfn",
68
+ "@aws-sdk/client-ssm",
69
+ "@aws-sdk/util-dynamodb",
70
+ ],
71
+ };
72
+ const commonEnv = {
73
+ NODE_OPTIONS: "--enable-source-maps",
74
+ SSM_PREFIX: props.ssmPrefix,
75
+ LOG_LEVEL: props.logLevel ?? "INFO",
76
+ POWERTOOLS_LOG_DEDUPLICATION_DISABLED: "true",
77
+ };
78
+ // ── SendEmailFn ────────────────────────────────────────────────────
79
+ this.sendEmailFn = new nodejs.NodejsFunction(this, "SendEmailFn", {
80
+ entry: path.join(handlersPath, "handlers/send-email.ts"),
81
+ handler: "handler",
82
+ runtime: lambda.Runtime.NODEJS_22_X,
83
+ memorySize: 256,
84
+ timeout: cdk.Duration.seconds(30),
85
+ environment: commonEnv,
86
+ bundling: commonBundling,
87
+ });
88
+ props.table.grantReadWriteData(this.sendEmailFn);
89
+ props.templateBucket.grantRead(this.sendEmailFn);
90
+ const stack = cdk.Stack.of(this);
91
+ this.sendEmailFn.addToRolePolicy(new iam.PolicyStatement({
92
+ actions: ["ses:SendEmail"],
93
+ resources: [
94
+ `arn:aws:ses:${stack.region}:${stack.account}:identity/*`,
95
+ `arn:aws:ses:${stack.region}:${stack.account}:configuration-set/${props.sesConfigSetName}`,
96
+ ],
97
+ }));
98
+ this.grantSsmRead(this.sendEmailFn, props.ssmPrefix);
99
+ // ── CheckConditionFn ───────────────────────────────────────────────
100
+ this.checkConditionFn = new nodejs.NodejsFunction(this, "CheckConditionFn", {
101
+ entry: path.join(handlersPath, "handlers/check-condition.ts"),
102
+ handler: "handler",
103
+ runtime: lambda.Runtime.NODEJS_22_X,
104
+ memorySize: 128,
105
+ timeout: cdk.Duration.seconds(10),
106
+ environment: commonEnv,
107
+ bundling: commonBundling,
108
+ });
109
+ props.table.grantReadData(this.checkConditionFn);
110
+ this.grantSsmRead(this.checkConditionFn, props.ssmPrefix);
111
+ // ── UnsubscribeFn ──────────────────────────────────────────────────
112
+ this.unsubscribeFn = new nodejs.NodejsFunction(this, "UnsubscribeFn", {
113
+ entry: path.join(handlersPath, "handlers/unsubscribe.ts"),
114
+ handler: "handler",
115
+ runtime: lambda.Runtime.NODEJS_22_X,
116
+ memorySize: 128,
117
+ timeout: cdk.Duration.seconds(10),
118
+ environment: commonEnv,
119
+ bundling: commonBundling,
120
+ });
121
+ props.table.grantReadWriteData(this.unsubscribeFn);
122
+ // ses:PutSuppressedDestination does not support resource-level permissions — * is required
123
+ this.unsubscribeFn.addToRolePolicy(new iam.PolicyStatement({
124
+ actions: ["ses:PutSuppressedDestination"],
125
+ resources: ["*"],
126
+ }));
127
+ this.grantSsmRead(this.unsubscribeFn, props.ssmPrefix);
128
+ const fnUrl = this.unsubscribeFn.addFunctionUrl({
129
+ authType: lambda.FunctionUrlAuthType.NONE,
130
+ });
131
+ this.unsubscribeFnUrl = fnUrl.url;
132
+ // ── BounceHandlerFn ────────────────────────────────────────────────
133
+ this.bounceHandlerFn = new nodejs.NodejsFunction(this, "BounceHandlerFn", {
134
+ entry: path.join(handlersPath, "handlers/bounce-handler.ts"),
135
+ handler: "handler",
136
+ runtime: lambda.Runtime.NODEJS_22_X,
137
+ memorySize: 128,
138
+ timeout: cdk.Duration.seconds(10),
139
+ environment: commonEnv,
140
+ bundling: commonBundling,
141
+ });
142
+ props.table.grantReadWriteData(this.bounceHandlerFn);
143
+ // ses:PutSuppressedDestination does not support resource-level permissions — * is required
144
+ this.bounceHandlerFn.addToRolePolicy(new iam.PolicyStatement({
145
+ actions: ["ses:PutSuppressedDestination"],
146
+ resources: ["*"],
147
+ }));
148
+ this.grantSsmRead(this.bounceHandlerFn, props.ssmPrefix);
149
+ // Subscribe to SES notifications
150
+ props.snsTopic.addSubscription(new snsSubscriptions.LambdaSubscription(this.bounceHandlerFn));
151
+ // ── EngagementHandlerFn ────────────────────────────────────────────
152
+ this.engagementHandlerFn = new nodejs.NodejsFunction(this, "EngagementHandlerFn", {
153
+ entry: path.join(handlersPath, "handlers/engagement-handler.ts"),
154
+ handler: "handler",
155
+ runtime: lambda.Runtime.NODEJS_22_X,
156
+ memorySize: 128,
157
+ timeout: cdk.Duration.seconds(10),
158
+ environment: commonEnv,
159
+ bundling: commonBundling,
160
+ });
161
+ props.eventsTable.grantWriteData(this.engagementHandlerFn);
162
+ this.grantSsmRead(this.engagementHandlerFn, props.ssmPrefix);
163
+ }
164
+ grantSsmRead(fn, prefix) {
165
+ fn.addToRolePolicy(new iam.PolicyStatement({
166
+ actions: ["ssm:GetParameter"],
167
+ resources: [
168
+ cdk.Arn.format({
169
+ service: "ssm",
170
+ resource: "parameter",
171
+ resourceName: `${prefix.replace(/^\//, "")}/*`,
172
+ }, cdk.Stack.of(this)),
173
+ ],
174
+ }));
175
+ }
176
+ }
177
+ exports.LambdasConstruct = LambdasConstruct;
178
+ //# sourceMappingURL=lambdas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambdas.js","sourceRoot":"","sources":["../../../lib/constructs/lambdas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import * as ses from "aws-cdk-lib/aws-ses";
2
+ import * as sns from "aws-cdk-lib/aws-sns";
3
+ import { Construct } from "constructs";
4
+ export interface SesConfigProps {
5
+ configSetName: string;
6
+ snsTopicName: string;
7
+ }
8
+ export declare class SesConfigConstruct extends Construct {
9
+ readonly configurationSet: ses.ConfigurationSet;
10
+ readonly snsTopic: sns.Topic;
11
+ readonly engagementTopic: sns.Topic;
12
+ constructor(scope: Construct, id: string, props: SesConfigProps);
13
+ }
14
+ //# sourceMappingURL=ses-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ses-config.d.ts","sourceRoot":"","sources":["../../../lib/constructs/ses-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,SAAgB,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,CAAC;IACvD,SAAgB,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC;IACpC,SAAgB,eAAe,EAAE,GAAG,CAAC,KAAK,CAAC;gBAE/B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc;CAkChE"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SesConfigConstruct = void 0;
37
+ const ses = __importStar(require("aws-cdk-lib/aws-ses"));
38
+ const sns = __importStar(require("aws-cdk-lib/aws-sns"));
39
+ const constructs_1 = require("constructs");
40
+ class SesConfigConstruct extends constructs_1.Construct {
41
+ configurationSet;
42
+ snsTopic;
43
+ engagementTopic;
44
+ constructor(scope, id, props) {
45
+ super(scope, id);
46
+ this.snsTopic = new sns.Topic(this, "SesNotificationsTopic", {
47
+ topicName: props.snsTopicName,
48
+ });
49
+ this.engagementTopic = new sns.Topic(this, "SesEngagementTopic", {
50
+ topicName: `${props.snsTopicName}-engagement`,
51
+ });
52
+ this.configurationSet = new ses.ConfigurationSet(this, "ConfigurationSet", {
53
+ configurationSetName: props.configSetName,
54
+ suppressionReasons: ses.SuppressionReasons.BOUNCES_AND_COMPLAINTS,
55
+ });
56
+ // Add SNS event destination for bounces and complaints
57
+ this.configurationSet.addEventDestination("BounceAndComplaint", {
58
+ destination: ses.EventDestination.snsTopic(this.snsTopic),
59
+ events: [ses.EmailSendingEvent.BOUNCE, ses.EmailSendingEvent.COMPLAINT],
60
+ });
61
+ // Add SNS event destination for engagement events
62
+ this.configurationSet.addEventDestination("Engagement", {
63
+ destination: ses.EventDestination.snsTopic(this.engagementTopic),
64
+ events: [
65
+ ses.EmailSendingEvent.DELIVERY,
66
+ ses.EmailSendingEvent.OPEN,
67
+ ses.EmailSendingEvent.CLICK,
68
+ ses.EmailSendingEvent.BOUNCE,
69
+ ses.EmailSendingEvent.COMPLAINT,
70
+ ],
71
+ });
72
+ }
73
+ }
74
+ exports.SesConfigConstruct = SesConfigConstruct;
75
+ //# sourceMappingURL=ses-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ses-config.js","sourceRoot":"","sources":["../../../lib/constructs/ses-config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yDAA2C;AAC3C,yDAA2C;AAC3C,2CAAuC;AAOvC,MAAa,kBAAmB,SAAQ,sBAAS;IAC/B,gBAAgB,CAAuB;IACvC,QAAQ,CAAY;IACpB,eAAe,CAAY;IAE3C,YAAY,KAAgB,EAAE,EAAU,EAAE,KAAqB;QAC7D,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEjB,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,uBAAuB,EAAE;YAC3D,SAAS,EAAE,KAAK,CAAC,YAAY;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,oBAAoB,EAAE;YAC/D,SAAS,EAAE,GAAG,KAAK,CAAC,YAAY,aAAa;SAC9C,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,EAAE;YACzE,oBAAoB,EAAE,KAAK,CAAC,aAAa;YACzC,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,CAAC,sBAAsB;SAClE,CAAC,CAAC;QAEH,uDAAuD;QACvD,IAAI,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,oBAAoB,EAAE;YAC9D,WAAW,EAAE,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YACzD,MAAM,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,iBAAiB,CAAC,SAAS,CAAC;SACxE,CAAC,CAAC;QAEH,kDAAkD;QAClD,IAAI,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,YAAY,EAAE;YACtD,WAAW,EAAE,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC;YAChE,MAAM,EAAE;gBACN,GAAG,CAAC,iBAAiB,CAAC,QAAQ;gBAC9B,GAAG,CAAC,iBAAiB,CAAC,IAAI;gBAC1B,GAAG,CAAC,iBAAiB,CAAC,KAAK;gBAC3B,GAAG,CAAC,iBAAiB,CAAC,MAAM;gBAC5B,GAAG,CAAC,iBAAiB,CAAC,SAAS;aAChC;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAvCD,gDAuCC"}
@@ -0,0 +1,17 @@
1
+ import { Construct } from "constructs";
2
+ export interface SsmConstructProps {
3
+ prefix: string;
4
+ tableName: string;
5
+ eventsTableName: string;
6
+ templateBucketName: string;
7
+ defaultFromEmail: string;
8
+ defaultFromName: string;
9
+ replyToEmail?: string;
10
+ sesConfigSetName: string;
11
+ unsubscribeBaseUrl: string;
12
+ unsubscribeSecret: string;
13
+ }
14
+ export declare class SsmConstruct extends Construct {
15
+ constructor(scope: Construct, id: string, props: SsmConstructProps);
16
+ }
17
+ //# sourceMappingURL=ssm-params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssm-params.d.ts","sourceRoot":"","sources":["../../../lib/constructs/ssm-params.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,YAAa,SAAQ,SAAS;gBAC7B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB;CAuBnE"}