@wraps.dev/cli 0.1.1 → 0.1.3

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.
@@ -1,165 +0,0 @@
1
- import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
2
- import type { SQSEvent } from "aws-lambda";
3
-
4
- const dynamodb = new DynamoDBClient({});
5
-
6
- /**
7
- * Lambda handler for processing SES events from SQS (via EventBridge)
8
- * Stores all SES events in DynamoDB:
9
- * - Send: Email sent from SES
10
- * - Delivery: Email delivered to recipient
11
- * - Open: Email opened by recipient
12
- * - Click: Link clicked in email
13
- * - Bounce: Email bounced (permanent or transient)
14
- * - Complaint: Recipient marked email as spam
15
- * - Reject: Email rejected before sending
16
- * - Rendering Failure: Template rendering failed
17
- * - DeliveryDelay: Temporary delivery delay
18
- * - Subscription: Recipient unsubscribed/changed preferences
19
- */
20
- export async function handler(event: SQSEvent) {
21
- console.log("Processing SES event from SQS:", JSON.stringify(event, null, 2));
22
-
23
- const tableName = process.env.TABLE_NAME;
24
- if (!tableName) {
25
- throw new Error("TABLE_NAME environment variable not set");
26
- }
27
-
28
- for (const record of event.Records) {
29
- try {
30
- // Parse the SQS message body (which contains the EventBridge event)
31
- const eventBridgeEvent = JSON.parse(record.body);
32
-
33
- // The actual SES event is in the 'detail' field of the EventBridge event
34
- const message = eventBridgeEvent.detail;
35
- const eventType = message.eventType || message.notificationType;
36
-
37
- // Extract email details
38
- const mail = message.mail;
39
- const messageId = mail.messageId;
40
- const mailTimestamp = new Date(mail.timestamp).getTime();
41
- const from = mail.source;
42
- const to = mail.destination;
43
- const subject = mail.commonHeaders?.subject || "";
44
-
45
- // Extract additional data and event-specific timestamp based on event type
46
- let additionalData: Record<string, unknown> = {};
47
- let eventTimestamp = mailTimestamp; // Default to mail timestamp
48
-
49
- if (eventType === "Send" && message.send) {
50
- // Send event uses mail timestamp
51
- additionalData = {
52
- tags: mail.tags || {},
53
- };
54
- } else if (eventType === "Delivery" && message.delivery) {
55
- eventTimestamp = new Date(message.delivery.timestamp).getTime();
56
- additionalData = {
57
- timestamp: message.delivery.timestamp,
58
- processingTimeMillis: message.delivery.processingTimeMillis,
59
- recipients: message.delivery.recipients,
60
- smtpResponse: message.delivery.smtpResponse,
61
- remoteMtaIp: message.delivery.remoteMtaIp,
62
- };
63
- } else if (eventType === "Open" && message.open) {
64
- eventTimestamp = new Date(message.open.timestamp).getTime();
65
- additionalData = {
66
- timestamp: message.open.timestamp,
67
- userAgent: message.open.userAgent,
68
- ipAddress: message.open.ipAddress,
69
- };
70
- } else if (eventType === "Click" && message.click) {
71
- eventTimestamp = new Date(message.click.timestamp).getTime();
72
- additionalData = {
73
- timestamp: message.click.timestamp,
74
- link: message.click.link,
75
- linkTags: message.click.linkTags || {},
76
- userAgent: message.click.userAgent,
77
- ipAddress: message.click.ipAddress,
78
- };
79
- } else if (eventType === "Bounce" && message.bounce) {
80
- eventTimestamp = new Date(message.bounce.timestamp).getTime();
81
- additionalData = {
82
- bounceType: message.bounce.bounceType,
83
- bounceSubType: message.bounce.bounceSubType,
84
- bouncedRecipients: message.bounce.bouncedRecipients,
85
- timestamp: message.bounce.timestamp,
86
- feedbackId: message.bounce.feedbackId,
87
- };
88
- } else if (eventType === "Complaint" && message.complaint) {
89
- eventTimestamp = new Date(message.complaint.timestamp).getTime();
90
- additionalData = {
91
- complainedRecipients: message.complaint.complainedRecipients,
92
- timestamp: message.complaint.timestamp,
93
- feedbackId: message.complaint.feedbackId,
94
- complaintFeedbackType: message.complaint.complaintFeedbackType,
95
- userAgent: message.complaint.userAgent,
96
- };
97
- } else if (eventType === "Reject" && message.reject) {
98
- // Reject doesn't have a specific timestamp, use mail timestamp
99
- additionalData = {
100
- reason: message.reject.reason,
101
- };
102
- } else if (eventType === "Rendering Failure" && message.failure) {
103
- // Rendering failure doesn't have a specific timestamp, use mail timestamp
104
- additionalData = {
105
- errorMessage: message.failure.errorMessage,
106
- templateName: message.failure.templateName,
107
- };
108
- } else if (eventType === "DeliveryDelay" && message.deliveryDelay) {
109
- eventTimestamp = new Date(message.deliveryDelay.timestamp).getTime();
110
- additionalData = {
111
- timestamp: message.deliveryDelay.timestamp,
112
- delayType: message.deliveryDelay.delayType,
113
- expirationTime: message.deliveryDelay.expirationTime,
114
- delayedRecipients: message.deliveryDelay.delayedRecipients,
115
- };
116
- } else if (eventType === "Subscription" && message.subscription) {
117
- eventTimestamp = new Date(message.subscription.timestamp).getTime();
118
- additionalData = {
119
- contactList: message.subscription.contactList,
120
- timestamp: message.subscription.timestamp,
121
- source: message.subscription.source,
122
- newTopicPreferences: message.subscription.newTopicPreferences,
123
- oldTopicPreferences: message.subscription.oldTopicPreferences,
124
- };
125
- }
126
-
127
- // Store event in DynamoDB
128
- // Use eventTimestamp as sort key to ensure each event type creates a unique record
129
- await dynamodb.send(
130
- new PutItemCommand({
131
- TableName: tableName,
132
- Item: {
133
- messageId: { S: messageId },
134
- sentAt: { N: eventTimestamp.toString() },
135
- accountId: { S: process.env.AWS_ACCOUNT_ID || "unknown" },
136
- from: { S: from },
137
- to: { SS: to },
138
- subject: { S: subject },
139
- eventType: { S: eventType },
140
- eventData: { S: JSON.stringify(message) },
141
- additionalData: { S: JSON.stringify(additionalData) },
142
- createdAt: { N: Date.now().toString() },
143
- expiresAt: {
144
- N: (Date.now() + 90 * 24 * 60 * 60 * 1000).toString(),
145
- }, // 90 days TTL
146
- },
147
- })
148
- );
149
-
150
- console.log(
151
- `Stored ${eventType} event for message ${messageId}`,
152
- additionalData
153
- );
154
- } catch (error) {
155
- console.error("Error processing record:", error);
156
- console.error("Record:", JSON.stringify(record, null, 2));
157
- // Don't throw - continue processing other records
158
- }
159
- }
160
-
161
- return {
162
- statusCode: 200,
163
- body: JSON.stringify({ message: "Events processed successfully" }),
164
- };
165
- }
@@ -1,13 +0,0 @@
1
- {
2
- "name": "wraps-email-event-processor",
3
- "version": "1.0.0",
4
- "description": "Lambda function to process SES events",
5
- "type": "module",
6
- "main": "index.ts",
7
- "dependencies": {
8
- "@aws-sdk/client-dynamodb": "^3.490.0"
9
- },
10
- "devDependencies": {
11
- "@types/aws-lambda": "^8.10.130"
12
- }
13
- }