@beesolve/email-service 0.1.1

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.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Email service
2
+
3
+ This repository contains CDK construct and SDK library for Email service which sends emails through AWS SES.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm i @beesolve/email-service
9
+ ```
package/dist/cdk.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { RemovalPolicy } from "aws-cdk-lib";
2
+ import { Function, FunctionOptions } from "aws-cdk-lib/aws-lambda";
3
+ import { ConfigurationSet, EmailSendingEvent } from "aws-cdk-lib/aws-ses";
4
+ import { Construct } from "constructs";
5
+ declare class Emails extends Construct {
6
+ private table;
7
+ private queue;
8
+ private bucket;
9
+ constructor(scope: Construct, id: string, props: {
10
+ readonly defaultSender: {
11
+ readonly name: string;
12
+ readonly emailAddress: string;
13
+ };
14
+ /**
15
+ * Set this if you want to set specific SES identity which will be used to send emails.
16
+ */
17
+ readonly fromArn?: string;
18
+ readonly defaultConfigurationSet?: ConfigurationSet;
19
+ /**
20
+ * Set this if you want to track other than default events.
21
+ *
22
+ * @default new Set([EmailSendingEvent.SEND, EmailSendingEvent.BOUNCE, EmailSendingEvent.COMPLAINT, EmailSendingEvent.DELIVERY, EmailSendingEvent.REJECT])
23
+ */
24
+ readonly eventsToTrack?: Set<EmailSendingEvent>;
25
+ readonly isProd?: boolean;
26
+ readonly removalPolicy?: RemovalPolicy;
27
+ readonly deletionProtection?: boolean;
28
+ /**
29
+ * How long should the attachments be stored in S3 bucket before they are deleted.
30
+ *
31
+ * @default 180
32
+ */
33
+ readonly attachmentsRetentionDays?: number;
34
+ /**
35
+ * How long should the messages be stored in DyanmoDB before they are deleted.
36
+ *
37
+ * If set to 0, messages are not being persisted to DynamoDB.
38
+ *
39
+ * @default 7
40
+ */
41
+ readonly messagesRetentionDays?: number;
42
+ /**
43
+ * Event bus which notifications about sent emails are emitted to.
44
+ *
45
+ * @default "default"
46
+ */
47
+ readonly eventBusName?: string;
48
+ /**
49
+ * @default
50
+ * {
51
+ * memorySize: 256,
52
+ * timeout: Duration.seconds(30),
53
+ * reservedConcurrentExecutions: 2
54
+ * }
55
+ */
56
+ readonly handler?: Pick<FunctionOptions, "memorySize" | "timeout" | "reservedConcurrentExecutions">;
57
+ });
58
+ readonly grantAccess: (grantee: Function) => void;
59
+ }
60
+ export { Emails };
package/dist/cdk.js ADDED
@@ -0,0 +1,139 @@
1
+ // packages/service-email/cdk.ts
2
+ import { SqsWithDlq } from "@beesolve/cdk-constructs";
3
+ import { Duration, RemovalPolicy } from "aws-cdk-lib";
4
+ import {
5
+ AttributeType,
6
+ Billing,
7
+ TableEncryptionV2,
8
+ TableV2
9
+ } from "aws-cdk-lib/aws-dynamodb";
10
+ import { EventBus } from "aws-cdk-lib/aws-events";
11
+ import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
12
+ import {
13
+ Architecture,
14
+ Runtime
15
+ } from "aws-cdk-lib/aws-lambda";
16
+ import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
17
+ import {
18
+ BlockPublicAccess,
19
+ Bucket,
20
+ BucketAccessControl,
21
+ BucketEncryption
22
+ } from "aws-cdk-lib/aws-s3";
23
+ import {
24
+ ConfigurationSet,
25
+ ConfigurationSetEventDestination,
26
+ EmailSendingEvent,
27
+ EventDestination
28
+ } from "aws-cdk-lib/aws-ses";
29
+ import { Construct } from "constructs";
30
+ import { resolve } from "node:path";
31
+ var __dirname = "/Users/ivan/data/work/github.com/beesolve/p/packages/packages/service-email";
32
+
33
+ class Emails extends Construct {
34
+ table;
35
+ queue;
36
+ bucket;
37
+ constructor(scope, id, props) {
38
+ super(scope, id);
39
+ const {
40
+ isProd = false,
41
+ attachmentsRetentionDays = 180,
42
+ messagesRetentionDays = 7,
43
+ eventBusName = "default",
44
+ defaultConfigurationSet = new ConfigurationSet(this, "DefaultConfigurationSet"),
45
+ eventsToTrack = new Set([
46
+ EmailSendingEvent.SEND,
47
+ EmailSendingEvent.BOUNCE,
48
+ EmailSendingEvent.COMPLAINT,
49
+ EmailSendingEvent.DELIVERY,
50
+ EmailSendingEvent.REJECT
51
+ ])
52
+ } = props;
53
+ const eventBus = EventBus.fromEventBusName(this, "EventBus", eventBusName);
54
+ new ConfigurationSetEventDestination(this, "SesEventDestination", {
55
+ events: Array.from(eventsToTrack),
56
+ configurationSet: defaultConfigurationSet,
57
+ destination: EventDestination.eventBus(eventBus),
58
+ enabled: true
59
+ });
60
+ this.table = new TableV2(this, "EmailLog", {
61
+ partitionKey: {
62
+ name: "pk",
63
+ type: AttributeType.STRING
64
+ },
65
+ billing: Billing.onDemand(),
66
+ deletionProtection: props.deletionProtection ?? false,
67
+ encryption: TableEncryptionV2.awsManagedKey(),
68
+ removalPolicy: props.removalPolicy ?? RemovalPolicy.RETAIN,
69
+ timeToLiveAttribute: "ttl",
70
+ pointInTimeRecoverySpecification: {
71
+ pointInTimeRecoveryEnabled: isProd
72
+ }
73
+ });
74
+ this.bucket = new Bucket(this, "EmailAttachments", {
75
+ accessControl: BucketAccessControl.PRIVATE,
76
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
77
+ encryption: BucketEncryption.S3_MANAGED,
78
+ enforceSSL: true,
79
+ lifecycleRules: [
80
+ {
81
+ expiration: Duration.days(attachmentsRetentionDays),
82
+ abortIncompleteMultipartUploadAfter: Duration.days(1)
83
+ }
84
+ ]
85
+ });
86
+ const handler = new NodejsFunction(this, "SqsHandler", {
87
+ description: "Email queue handler",
88
+ entry: resolve(__dirname, "./src/handler.ts"),
89
+ handler: "handler",
90
+ bundling: {
91
+ minify: isProd,
92
+ sourceMap: isProd,
93
+ sourcesContent: false,
94
+ target: "es2022"
95
+ },
96
+ memorySize: props.handler?.memorySize ?? 256,
97
+ timeout: props.handler?.timeout ?? Duration.seconds(30),
98
+ reservedConcurrentExecutions: props.handler?.reservedConcurrentExecutions ?? 2,
99
+ runtime: Runtime.NODEJS_24_X,
100
+ architecture: Architecture.ARM_64,
101
+ environment: {
102
+ BUCKET_NAME: this.bucket.bucketName,
103
+ TABLE_NAME: this.table.tableName,
104
+ DEFAULT_SENDER_NAME: props.defaultSender.name,
105
+ DEFAULT_SENDER_EMAIL_ADDRESS: props.defaultSender.emailAddress,
106
+ MESSAGES_RETENTION_DAYS: String(messagesRetentionDays),
107
+ EVENT_BUS_ARN: eventBus.eventBusArn,
108
+ DEFAULT_CONFIGURATION_SET_NAME: defaultConfigurationSet.configurationSetName
109
+ },
110
+ depsLockFilePath: resolve(`${__dirname}/../../bun.lock`)
111
+ });
112
+ this.table.grantReadWriteData(handler);
113
+ this.bucket.grantRead(handler);
114
+ eventBus.grantPutEventsTo(handler);
115
+ if (props.fromArn) {
116
+ handler.addEnvironment("FROM_ARN", props.fromArn);
117
+ }
118
+ handler.addToRolePolicy(new PolicyStatement({
119
+ actions: ["ses:SendEmail", "ses:SendRawEmail"],
120
+ resources: ["*"],
121
+ effect: Effect.ALLOW
122
+ }));
123
+ const { queue } = SqsWithDlq.asLambdaInput({
124
+ lambda: handler
125
+ });
126
+ this.queue = queue;
127
+ }
128
+ grantAccess = (grantee) => {
129
+ this.table.grantReadData(grantee);
130
+ this.queue.grantSendMessages(grantee);
131
+ this.bucket.grantWrite(grantee);
132
+ grantee.addEnvironment("BEESOLVE_EMAILS_QUEUE_URL", this.queue.queueUrl);
133
+ grantee.addEnvironment("BEESOLVE_EMAILS_TABLE_NAME", this.table.tableName);
134
+ grantee.addEnvironment("BEESOLVE_EMAILS_ATTACHMENTS_BUCKET", this.bucket.bucketName);
135
+ };
136
+ }
137
+ export {
138
+ Emails
139
+ };
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { SQSClient } from "@aws-sdk/client-sqs";
3
+ import * as v from "valibot";
4
+ declare const requestSchema: unknown;
5
+ type Attachment = {
6
+ readonly type: "public";
7
+ readonly mimeType: string;
8
+ readonly publicUrl: string;
9
+ readonly customName: string;
10
+ } | {
11
+ readonly type: "s3";
12
+ readonly mimeType: string;
13
+ readonly body: Buffer;
14
+ readonly customName: string;
15
+ };
16
+ interface Sender {
17
+ readonly name: string;
18
+ readonly emailAddress: string;
19
+ }
20
+ declare class Email {
21
+ private readonly props;
22
+ private readonly s3Client;
23
+ private readonly sqsClient;
24
+ constructor(props?: {
25
+ readonly s3Client?: S3Client;
26
+ readonly sqsClient?: SQSClient;
27
+ });
28
+ readonly sendEmail: (request: {
29
+ readonly recipients: string[];
30
+ readonly subject: string;
31
+ readonly html: string;
32
+ readonly text?: string;
33
+ readonly sender?: Sender;
34
+ readonly attachments?: Attachment[];
35
+ readonly configurationSetName?: string;
36
+ }) => Promise<{
37
+ requestId: string;
38
+ }>;
39
+ readonly getMessage: (requestId: string) => Promise<{
40
+ readonly requestId: string;
41
+ readonly messageId: string;
42
+ readonly request: v.InferOutput<typeof requestSchema>;
43
+ readonly expiresAt: Date;
44
+ }>;
45
+ }
46
+ export { Email };
package/dist/sdk.js ADDED
@@ -0,0 +1,142 @@
1
+ // packages/service-email/sdk.ts
2
+ import { assertUnreachable, call } from "@beesolve/helpers";
3
+ import { PutObjectCommand } from "@aws-sdk/client-s3";
4
+ import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
5
+ import { GetCommand } from "@aws-sdk/lib-dynamodb";
6
+ import { randomBytes } from "node:crypto";
7
+ import * as v2 from "valibot";
8
+
9
+ // packages/service-email/src/aws.ts
10
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
11
+ import { S3Client } from "@aws-sdk/client-s3";
12
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
13
+ var dynamoClient = DynamoDBDocumentClient.from(new DynamoDBClient, {
14
+ marshallOptions: {
15
+ removeUndefinedValues: true,
16
+ convertEmptyValues: false
17
+ }
18
+ });
19
+ var s3Client = new S3Client;
20
+
21
+ // packages/service-email/src/validation.ts
22
+ import * as v from "valibot";
23
+ var requestSchema = v.object({
24
+ id: v.string(),
25
+ recipients: v.array(v.pipe(v.string(), v.email())),
26
+ subject: v.string(),
27
+ html: v.string(),
28
+ text: v.optional(v.string()),
29
+ sender: v.optional(v.object({
30
+ name: v.string(),
31
+ emailAddress: v.string()
32
+ })),
33
+ attachments: v.optional(v.array(v.variant("type", [
34
+ v.object({
35
+ type: v.literal("public"),
36
+ mimeType: v.string(),
37
+ publicUrl: v.string(),
38
+ customName: v.string()
39
+ }),
40
+ v.object({
41
+ type: v.literal("s3"),
42
+ mimeType: v.string(),
43
+ fileId: v.string(),
44
+ customName: v.string()
45
+ })
46
+ ]))),
47
+ configurationSetName: v.optional(v.string())
48
+ });
49
+
50
+ // packages/service-email/sdk.ts
51
+ var message = `It seems that Emails service has not been set up correctly. Please make sure you've used official CDK construct and that you've granted access to your labmda function.`;
52
+ var envSchema = v2.object({
53
+ BEESOLVE_EMAILS_QUEUE_URL: v2.config(v2.string(), { message }),
54
+ BEESOLVE_EMAILS_TABLE_NAME: v2.config(v2.string(), { message }),
55
+ BEESOLVE_EMAILS_ATTACHMENTS_BUCKET: v2.config(v2.string(), { message })
56
+ });
57
+ var env = v2.parse(envSchema, process.env);
58
+ var sqsClient = new SQSClient({});
59
+
60
+ class Email {
61
+ props;
62
+ s3Client;
63
+ sqsClient;
64
+ constructor(props = {}) {
65
+ this.props = props;
66
+ this.s3Client = props.s3Client ?? s3Client;
67
+ this.sqsClient = props.sqsClient ?? sqsClient;
68
+ }
69
+ sendEmail = async (request) => {
70
+ const id = randomBytes(32).toString("base64url");
71
+ const { attachments, ...rest } = request;
72
+ const resolvedAttachments = attachments ? await call(async () => {
73
+ return Promise.all(attachments.map(async (item) => {
74
+ if (item.type === "public")
75
+ return item;
76
+ if (item.type === "s3") {
77
+ const fileId = randomBytes(32).toString("base64url");
78
+ const { body, ...rest2 } = item;
79
+ await this.s3Client.send(new PutObjectCommand({
80
+ Bucket: env.BEESOLVE_EMAILS_ATTACHMENTS_BUCKET,
81
+ Key: fileId,
82
+ Body: body
83
+ }));
84
+ return {
85
+ ...rest2,
86
+ fileId
87
+ };
88
+ }
89
+ assertUnreachable(item);
90
+ }));
91
+ }) : undefined;
92
+ await this.sqsClient.send(new SendMessageCommand({
93
+ QueueUrl: env.BEESOLVE_EMAILS_QUEUE_URL,
94
+ MessageBody: JSON.stringify({
95
+ id,
96
+ ...rest,
97
+ attachments: resolvedAttachments
98
+ })
99
+ }));
100
+ return { requestId: id };
101
+ };
102
+ getMessage = async (requestId) => {
103
+ const { Item } = await dynamoClient.send(new GetCommand({
104
+ TableName: env.BEESOLVE_EMAILS_TABLE_NAME,
105
+ Key: {
106
+ pk: requestId
107
+ }
108
+ }));
109
+ if (Item == null) {
110
+ throw new MessageNotFoundError(`Message for ${requestId} has not been found. Make sure you have set up messagesRetentionDays properly.`);
111
+ }
112
+ const result = v2.safeParse(requestSchema, Item.request);
113
+ if (!result.success) {
114
+ throw new MalformedRequestError(`Persisted request is malformed.`);
115
+ }
116
+ return {
117
+ requestId,
118
+ messageId: Item.messageId,
119
+ request: result.output,
120
+ expiresAt: new Date(Item.ttl * 1000)
121
+ };
122
+ };
123
+ }
124
+
125
+ class MessageNotFoundError extends Error {
126
+ stringified;
127
+ constructor(message2) {
128
+ super(typeof message2 === "string" ? message2 : JSON.stringify(message2));
129
+ this.stringified = typeof message2 !== "string";
130
+ }
131
+ }
132
+
133
+ class MalformedRequestError extends Error {
134
+ stringified;
135
+ constructor(message2) {
136
+ super(typeof message2 === "string" ? message2 : JSON.stringify(message2));
137
+ this.stringified = typeof message2 !== "string";
138
+ }
139
+ }
140
+ export {
141
+ Email
142
+ };
@@ -0,0 +1,80 @@
1
+ import { JSX, ReactNode } from "react";
2
+ declare function BaseLayout<TStyles extends EmailBaseStyles>(props: {
3
+ readonly children: (styles: TStyles) => ReactNode;
4
+ readonly previewText: string;
5
+ readonly enhanceStyles?: (styles: EmailBaseStyles) => TStyles;
6
+ readonly project: {
7
+ readonly name: ReactNode;
8
+ readonly logo?: ReactNode;
9
+ readonly baseUri: string;
10
+ };
11
+ readonly notice?: (styles: TStyles) => ReactNode;
12
+ readonly notificationSettings?: (styles: TStyles) => ReactNode;
13
+ }): JSX.Element;
14
+ declare const baseStyles: {
15
+ readonly h1: {
16
+ readonly color: "#333";
17
+ readonly fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif";
18
+ readonly fontSize: "20px";
19
+ readonly fontWeight: "bold";
20
+ readonly marginBottom: "15px";
21
+ };
22
+ readonly link: {
23
+ readonly color: "#2754C5";
24
+ readonly fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif";
25
+ readonly fontSize: "14px";
26
+ readonly textDecoration: "underline";
27
+ };
28
+ readonly mainText: {
29
+ readonly marginBottom: "14px";
30
+ };
31
+ readonly main: {
32
+ readonly backgroundColor: "#fff";
33
+ readonly color: "#212121";
34
+ };
35
+ readonly container: {
36
+ readonly padding: "20px";
37
+ readonly margin: "0 auto";
38
+ readonly backgroundColor: "#eee";
39
+ };
40
+ readonly imageSection: {
41
+ readonly backgroundColor: "#252f3d";
42
+ readonly padding: "20px 0";
43
+ };
44
+ readonly coverSection: {
45
+ readonly backgroundColor: "#fff";
46
+ };
47
+ readonly upperSection: {
48
+ readonly padding: "25px 35px";
49
+ };
50
+ readonly lowerSection: {
51
+ readonly padding: "25px 35px";
52
+ };
53
+ readonly footerText: {
54
+ readonly fontSize: "12px";
55
+ readonly padding: "0 20px";
56
+ };
57
+ readonly cautionText: {
58
+ readonly margin: "0px";
59
+ };
60
+ readonly button: {
61
+ readonly fontSize: "14px";
62
+ readonly backgroundColor: "#1976d2";
63
+ readonly color: "#fff";
64
+ readonly lineHeight: 1.5;
65
+ readonly borderRadius: "0.5em";
66
+ readonly padding: "12px 24px";
67
+ readonly fontFamily: "Roboto,Arial,sans-serif";
68
+ readonly margin: "30px auto";
69
+ };
70
+ };
71
+ type EmailBaseStyles = typeof baseStyles;
72
+ import { Attributes, FunctionComponent } from "react";
73
+ declare function renderEmail<P extends {}>(props: {
74
+ readonly template: FunctionComponent<P>;
75
+ readonly props?: (Attributes & P) | null;
76
+ }): Promise<{
77
+ readonly html: string;
78
+ readonly text: string;
79
+ }>;
80
+ export { renderEmail, EmailBaseStyles, BaseLayout };
@@ -0,0 +1,166 @@
1
+ // packages/service-email/src/template/layout.tsx
2
+ import {
3
+ Body,
4
+ Column,
5
+ Container,
6
+ Head,
7
+ Hr,
8
+ Html,
9
+ Link,
10
+ Preview,
11
+ Row,
12
+ Section,
13
+ Text
14
+ } from "@react-email/components";
15
+ import { jsxDEV } from "react/jsx-dev-runtime";
16
+ function BaseLayout(props) {
17
+ const styles = props.enhanceStyles?.(baseStyles) ?? baseStyles;
18
+ return /* @__PURE__ */ jsxDEV(Html, {
19
+ children: [
20
+ /* @__PURE__ */ jsxDEV(Head, {}, undefined, false, undefined, this),
21
+ /* @__PURE__ */ jsxDEV(Body, {
22
+ style: styles.main,
23
+ children: [
24
+ /* @__PURE__ */ jsxDEV(Preview, {
25
+ children: props.previewText
26
+ }, undefined, false, undefined, this),
27
+ /* @__PURE__ */ jsxDEV(Container, {
28
+ style: styles.container,
29
+ children: [
30
+ /* @__PURE__ */ jsxDEV(Section, {
31
+ style: styles.coverSection,
32
+ children: [
33
+ /* @__PURE__ */ jsxDEV(Section, {
34
+ style: styles.imageSection,
35
+ children: /* @__PURE__ */ jsxDEV(Row, {
36
+ children: /* @__PURE__ */ jsxDEV(Column, {
37
+ children: /* @__PURE__ */ jsxDEV(Text, {
38
+ style: {
39
+ color: "#fff",
40
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
41
+ fontSize: "28px",
42
+ margin: "24px 0",
43
+ fontWeight: "bold",
44
+ textAlign: "center",
45
+ lineHeight: "1.3em"
46
+ },
47
+ children: props.project.logo ?? props.project.name
48
+ }, undefined, false, undefined, this)
49
+ }, undefined, false, undefined, this)
50
+ }, undefined, false, undefined, this)
51
+ }, undefined, false, undefined, this),
52
+ /* @__PURE__ */ jsxDEV(Section, {
53
+ style: styles.upperSection,
54
+ children: props.children(styles)
55
+ }, undefined, false, undefined, this),
56
+ /* @__PURE__ */ jsxDEV(Hr, {}, undefined, false, undefined, this),
57
+ props.notice?.(styles) ?? /* @__PURE__ */ jsxDEV(Section, {
58
+ style: styles.lowerSection,
59
+ children: /* @__PURE__ */ jsxDEV(Text, {
60
+ style: styles.cautionText,
61
+ children: [
62
+ /* @__PURE__ */ jsxDEV("strong", {
63
+ children: props.project.name
64
+ }, undefined, false, undefined, this),
65
+ " service will never email you and ask you to disclose or verify your password, credit card, or banking account number."
66
+ ]
67
+ }, undefined, true, undefined, this)
68
+ }, undefined, false, undefined, this)
69
+ ]
70
+ }, undefined, true, undefined, this),
71
+ props.notificationSettings?.(styles) ?? /* @__PURE__ */ jsxDEV(Text, {
72
+ style: styles.footerText,
73
+ children: [
74
+ "You have received this message because you are using",
75
+ " ",
76
+ /* @__PURE__ */ jsxDEV("strong", {
77
+ children: props.project.name
78
+ }, undefined, false, undefined, this),
79
+ " service. If you don't want to receive these emails please",
80
+ " ",
81
+ /* @__PURE__ */ jsxDEV(Link, {
82
+ href: `${props.project.baseUri}/app/settings`,
83
+ target: "_blank",
84
+ style: styles.link,
85
+ children: "manage your notification setting"
86
+ }, undefined, false, undefined, this),
87
+ "."
88
+ ]
89
+ }, undefined, true, undefined, this)
90
+ ]
91
+ }, undefined, true, undefined, this)
92
+ ]
93
+ }, undefined, true, undefined, this)
94
+ ]
95
+ }, undefined, true, undefined, this);
96
+ }
97
+ var text = {
98
+ color: "#333",
99
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
100
+ fontSize: "14px",
101
+ margin: "24px 0"
102
+ };
103
+ var baseStyles = {
104
+ h1: {
105
+ color: "#333",
106
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
107
+ fontSize: "20px",
108
+ fontWeight: "bold",
109
+ marginBottom: "15px"
110
+ },
111
+ link: {
112
+ color: "#2754C5",
113
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
114
+ fontSize: "14px",
115
+ textDecoration: "underline"
116
+ },
117
+ text,
118
+ mainText: { ...text, marginBottom: "14px" },
119
+ main: {
120
+ backgroundColor: "#fff",
121
+ color: "#212121"
122
+ },
123
+ container: {
124
+ padding: "20px",
125
+ margin: "0 auto",
126
+ backgroundColor: "#eee"
127
+ },
128
+ imageSection: {
129
+ backgroundColor: "#252f3d",
130
+ padding: "20px 0"
131
+ },
132
+ coverSection: { backgroundColor: "#fff" },
133
+ upperSection: { padding: "25px 35px" },
134
+ lowerSection: { padding: "25px 35px" },
135
+ footerText: {
136
+ ...text,
137
+ fontSize: "12px",
138
+ padding: "0 20px"
139
+ },
140
+ cautionText: { ...text, margin: "0px" },
141
+ button: {
142
+ fontSize: "14px",
143
+ backgroundColor: "#1976d2",
144
+ color: "#fff",
145
+ lineHeight: 1.5,
146
+ borderRadius: "0.5em",
147
+ padding: "12px 24px",
148
+ fontFamily: "Roboto,Arial,sans-serif",
149
+ margin: "30px auto"
150
+ }
151
+ };
152
+ // packages/service-email/src/template/render.ts
153
+ import { render, toPlainText } from "@react-email/components";
154
+ import { createElement } from "react";
155
+ async function renderEmail(props) {
156
+ const html = await render(createElement(props.template, props.props));
157
+ const text2 = toPlainText(html);
158
+ return {
159
+ html,
160
+ text: text2
161
+ };
162
+ }
163
+ export {
164
+ renderEmail,
165
+ BaseLayout
166
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@beesolve/email-service",
3
+ "description": "",
4
+ "version": "0.1.1",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "exports": {
10
+ "./cdk": {
11
+ "import": {
12
+ "types": "./dist/cdk.d.ts",
13
+ "default": "./dist/cdk.js"
14
+ }
15
+ },
16
+ "./sdk": {
17
+ "import": {
18
+ "types": "./dist/sdk.d.ts",
19
+ "default": "./dist/sdk.js"
20
+ }
21
+ },
22
+ "./templating": {
23
+ "import": {
24
+ "types": "./dist/templating.d.ts",
25
+ "default": "./dist/templating.js"
26
+ }
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "homepage": "https://github.com/beesolve/packages/tree/main/packages/email-service#readme",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/beesolve/packages.git"
35
+ },
36
+ "scripts": {
37
+ "type-check": "tsc --noEmit"
38
+ },
39
+ "peerDependencies": {
40
+ "typescript": "^5.9.3"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "typescript": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "dependencies": {
48
+ "@aws-sdk/client-eventbridge": "^3.978.0",
49
+ "@aws-sdk/client-dynamodb": "^3.978.0",
50
+ "@aws-sdk/client-s3": "^3.978.0",
51
+ "@aws-sdk/client-ses": "^3.978.0",
52
+ "@aws-sdk/client-sqs": "^3.978.0",
53
+ "@aws-sdk/lib-dynamodb": "^3.978.0",
54
+ "@react-email/components": "1.0.6",
55
+ "aws-cdk-lib": "^2.236.0",
56
+ "constructs": "^10.4.5",
57
+ "mimetext": "^3.0.27",
58
+ "valibot": "^1.2.0",
59
+ "react": "^19.2.4",
60
+ "@beesolve/helpers": "0.1.1",
61
+ "@beesolve/cdk-constructs": "0.1.1"
62
+ },
63
+ "devDependencies": {
64
+ "@types/react": "^19.2.10"
65
+ }
66
+ }