|
1 | 1 | import { SendRawEmailCommand } from "@aws-sdk/client-ses"; |
2 | 2 | import { encode } from "base64-arraybuffer"; |
| 3 | +import { AvailableSQSFunctions, SQSPayload } from "common/types/sqsMessage.js"; |
3 | 4 |
|
4 | 5 | /** |
5 | 6 | * Generates a SendRawEmailCommand for SES to send an email with an attached membership pass. |
@@ -132,3 +133,134 @@ ${encodedAttachment} |
132 | 133 | }, |
133 | 134 | }); |
134 | 135 | } |
| 136 | + |
| 137 | +/** |
| 138 | + * Generates a SendRawEmailCommand for SES to send a sales confirmation email |
| 139 | + * |
| 140 | + * @param payload - The SQS Payload for sending sale emails |
| 141 | + * @param senderEmail - The email address of the sender with a verified identity in SES. |
| 142 | + * @param imageBuffer - The normal image ticket/pass in ArrayBufferLike format. |
| 143 | + * @returns The command to send the email via SES. |
| 144 | + */ |
| 145 | +export function generateSalesEmail( |
| 146 | + payload: SQSPayload<AvailableSQSFunctions.SendSaleEmail>["payload"], |
| 147 | + senderEmail: string, |
| 148 | + imageBuffer: ArrayBufferLike, |
| 149 | +): SendRawEmailCommand { |
| 150 | + const encodedImage = encode(imageBuffer); |
| 151 | + const boundary = "----BoundaryForEmail"; |
| 152 | + |
| 153 | + const subject = `Your ${payload.type === "merch" ? "order" : "ticket"} has been confirmed!`; |
| 154 | + |
| 155 | + const emailTemplate = ` |
| 156 | +<!doctype html> |
| 157 | +<html> |
| 158 | +<head> |
| 159 | + <title>${subject}</title> |
| 160 | + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| 161 | + <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"> |
| 162 | + <base target="_blank"> |
| 163 | + <style> |
| 164 | + body { |
| 165 | + background-color: #F0F1F3; |
| 166 | + font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, sans-serif; |
| 167 | + font-size: 15px; |
| 168 | + line-height: 26px; |
| 169 | + margin: 0; |
| 170 | + color: #444; |
| 171 | + } |
| 172 | + .wrap { |
| 173 | + background-color: #fff; |
| 174 | + padding: 30px; |
| 175 | + max-width: 525px; |
| 176 | + margin: 0 auto; |
| 177 | + border-radius: 5px; |
| 178 | + } |
| 179 | + .button { |
| 180 | + background: #0055d4; |
| 181 | + border-radius: 3px; |
| 182 | + text-decoration: none !important; |
| 183 | + color: #fff !important; |
| 184 | + font-weight: bold; |
| 185 | + padding: 10px 30px; |
| 186 | + display: inline-block; |
| 187 | + } |
| 188 | + .button:hover { |
| 189 | + background: #111; |
| 190 | + } |
| 191 | + .footer { |
| 192 | + text-align: center; |
| 193 | + font-size: 12px; |
| 194 | + color: #888; |
| 195 | + } |
| 196 | + img { |
| 197 | + max-width: 100%; |
| 198 | + height: auto; |
| 199 | + } |
| 200 | + a { |
| 201 | + color: #0055d4; |
| 202 | + } |
| 203 | + a:hover { |
| 204 | + color: #111; |
| 205 | + } |
| 206 | + @media screen and (max-width: 600px) { |
| 207 | + .wrap { |
| 208 | + max-width: auto; |
| 209 | + } |
| 210 | + } |
| 211 | + </style> |
| 212 | +</head> |
| 213 | +<body> |
| 214 | + <div class="gutter" style="padding: 30px;"> </div> |
| 215 | + <img src="https://acm-brand-images.s3.amazonaws.com/banner-blue.png" style="height: 100px; width: 210px; align-self: center;"/> |
| 216 | + <br /> |
| 217 | + <div class="wrap"> |
| 218 | + <h2 style="text-align: center;">${subject}</h2> |
| 219 | + <p> |
| 220 | + Thank you for your purchase of ${payload.quantity} ${payload.itemName} ${payload.size ? `(size ${payload.size})` : ""}. |
| 221 | + ${payload.type === "merch" ? "When picking up your order" : "When attending the event"}, show the attached QR code to our staff to verify your purchase. |
| 222 | + </p> |
| 223 | + ${payload.customText ? `<p>${payload.customText}</p>` : ""} |
| 224 | + <p> |
| 225 | + If you have any questions, feel free to ask on our Discord! |
| 226 | + </p> |
| 227 | + <div style="text-align: center; margin-top: 20px;"> |
| 228 | + <a href="https://www.acm.illinois.edu/resources" class="button">ACM @ UIUC Resources</a> |
| 229 | + </div> |
| 230 | + </div> |
| 231 | + <div class="footer"> |
| 232 | + <p> |
| 233 | + <a href="https://acm.illinois.edu">ACM @ UIUC Homepage</a> |
| 234 | + <a href="mailto:[email protected]">Email ACM @ UIUC</a> |
| 235 | + </p> |
| 236 | + </div> |
| 237 | +</body> |
| 238 | +</html> |
| 239 | + `; |
| 240 | + |
| 241 | + const rawEmail = ` |
| 242 | +MIME-Version: 1.0 |
| 243 | +Content-Type: multipart/mixed; boundary="${boundary}" |
| 244 | +From: ACM @ UIUC <${senderEmail}> |
| 245 | +To: ${payload.email} |
| 246 | +Subject: ${subject} |
| 247 | +
|
| 248 | +--${boundary} |
| 249 | +Content-Type: text/html; charset="UTF-8" |
| 250 | +
|
| 251 | +${emailTemplate} |
| 252 | +
|
| 253 | +--${boundary} |
| 254 | +Content-Type: image/png |
| 255 | +Content-Transfer-Encoding: base64 |
| 256 | +Content-Disposition: attachment; filename="${payload.itemName}.png" |
| 257 | +
|
| 258 | +${encodedImage} |
| 259 | +--${boundary}--`.trim(); |
| 260 | + |
| 261 | + return new SendRawEmailCommand({ |
| 262 | + RawMessage: { |
| 263 | + Data: new TextEncoder().encode(rawEmail), |
| 264 | + }, |
| 265 | + }); |
| 266 | +} |
0 commit comments