{"id":19551416,"url":"https://github.com/jacoblincool/cloudflare-email-kit","last_synced_at":"2025-05-08T20:56:20.687Z","repository":{"id":205569057,"uuid":"714408773","full_name":"JacobLinCool/cloudflare-email-kit","owner":"JacobLinCool","description":"The Cloudflare Email Kit is a powerful toolkit designed to handle both incoming and outgoing emails with ease on Cloudflare Workers, providing developers with a modular approach to integrating email functionalities within serverless applications.","archived":false,"fork":false,"pushed_at":"2025-05-03T15:39:54.000Z","size":379,"stargazers_count":28,"open_issues_count":12,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-03T16:46:35.472Z","etag":null,"topics":["cloudflare","cloudflare-workers","email","router","workers"],"latest_commit_sha":null,"homepage":"http://jacoblin.cool/cloudflare-email-kit/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JacobLinCool.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-11-04T20:14:50.000Z","updated_at":"2025-03-18T01:34:41.000Z","dependencies_parsed_at":"2023-12-27T09:28:37.507Z","dependency_job_id":"63fdfc2d-97fd-4a17-8b0a-89fbc63741a6","html_url":"https://github.com/JacobLinCool/cloudflare-email-kit","commit_stats":{"total_commits":54,"total_committers":3,"mean_commits":18.0,"dds":0.2777777777777778,"last_synced_commit":"1b464a36b188ccb673ee2af9adbec5b29e41eafd"},"previous_names":["jacoblincool/cloudflare-email-kit"],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobLinCool%2Fcloudflare-email-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobLinCool%2Fcloudflare-email-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobLinCool%2Fcloudflare-email-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobLinCool%2Fcloudflare-email-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JacobLinCool","download_url":"https://codeload.github.com/JacobLinCool/cloudflare-email-kit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253149409,"owners_count":21861718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","cloudflare-workers","email","router","workers"],"created_at":"2024-11-11T04:13:49.191Z","updated_at":"2025-05-08T20:56:20.664Z","avatar_url":"https://github.com/JacobLinCool.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cloudflare Email Kit\n\nThe Cloudflare Email Kit is a powerful toolkit designed to handle both incoming and outgoing emails with ease on Cloudflare Workers. It emphasizes ease of use and composability, providing developers with a modular approach to integrating email functionalities within their serverless applications.\n\n## Features\n\n- **User-Friendly Middleware:** Employ an intuitive middleware system that streamlines email processing.\n- **Composable Routing:** Craft complex email handling logic with a highly composable EmailRouter.\n- **Cloudflare Integrations:** Store emails effortlessly with Cloudflare’s R2 Object Storage and D1 Database support.\n- **Workload Management:** Utilize Cloudflare Queues to efficiently distribute email processing tasks.\n- **Email Utilities:** Simplify the art of parsing, crafting and sending emails with a set of convenient utilities.\n\n## Getting Started\n\nBegin using the Cloudflare Email Kit with a simple installation:\n\n```sh\npnpm install cloudflare-email\n```\n\n\u003e The `cloudflare-email` package contains the core functionalities of the Cloudflare Email Kit. For additional features, install the packages listed in the documentation.\n\n## Examples\n\n### Router\n\nNavigate the complexities of email routing with customizable rules and actions.\n\nSee [examples/router](examples/router/).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eSee the example code\u003c/summary\u003e\n\n```ts\nimport { CATCH_ALL, EmailKit, EmailRouter, REJECT_ALL, SizeGuard, respond } from \"cloudflare-email\";\nimport { Backup } from \"cloudflare-email-backup\";\n\nexport interface Env {\n    R2: R2Bucket;\n    D1: D1Database;\n    NOTIFICATION_EMAIL: string;\n}\n\nexport default {\n    async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext): Promise\u003cvoid\u003e {\n        const router = new EmailRouter()\n            // handle auto-sent emails\n            .match(\n                (m) =\u003e m.isAuto(),\n                (m) =\u003e m.forward(env.NOTIFICATION_EMAIL),\n            )\n            // use a sub-router to handle subdomain emails\n            .match(\n                /@test\\.csie\\.cool$/,\n                new EmailRouter()\n                    .match(/^admin@/, async (message) =\u003e {\n                        const msg = respond(message);\n                        msg.addMessage({\n                            contentType: \"text/plain\",\n                            data: \"Hello, I'm the admin!\",\n                        });\n                        await message.reply(msg);\n                    })\n                    .match(\n                        // function matchers are also supported, even async ones which query databases\n                        (m) =\u003e m.from.length % 2 === 0,\n                        async (message) =\u003e {\n                            const msg = respond(message);\n                            msg.addMessage({\n                                contentType: \"text/plain\",\n                                data: `The length of your email address is even!`,\n                            });\n                            await message.reply(msg);\n                        },\n                    )\n                    .match(CATCH_ALL, async (message) =\u003e {\n                        const msg = respond(message);\n                        msg.addMessage({\n                            contentType: \"text/plain\",\n                            data: \"The length of your email address is odd!\",\n                        });\n                        await message.reply(msg);\n                    }),\n            )\n            .match(...REJECT_ALL(\"Your email is rejected! :P\"));\n\n        const kit = new EmailKit()\n            .use(new SizeGuard(10 * 1024 * 1024))\n            .use(\n                new Backup({\n                    bucket: env.R2,\n                    prefix: \"backup\",\n                    database: env.D1,\n                    table: \"emails\",\n                }),\n            )\n            .use(router);\n\n        await kit.process(message);\n    },\n};\n```\n\n\u003c/details\u003e\n\n### Parser\n\nEffortlessly parse and interact with email content and attachments.\n\nSee [examples/parser](examples/parser/).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eSee the example code\u003c/summary\u003e\n\n```ts\nimport { EmailKit, SizeGuard, respond } from \"cloudflare-email\";\nimport { ParsedContext, Parser } from \"cloudflare-email-parser\";\n\nexport interface Env {\n    R2: R2Bucket;\n}\n\nexport default {\n    async email(message: ForwardableEmailMessage, env: Env): Promise\u003cvoid\u003e {\n        const kit = new EmailKit()\n            .use(new SizeGuard(10 * 1024 * 1024))\n            .use(new Parser())\n            .use({\n                name: \"save-attechments\",\n                async handle(ctx: ParsedContext) {\n                    for (const attachment of ctx.parsed.attachments) {\n                        const { filename, content, mimeType } = attachment;\n                        const key = `attachments/${ctx.parsed.messageId}/${filename}`;\n                        console.log(`Saving attachment ${filename} to ${key} ...`);\n                        await env.R2.put(key, content, {\n                            customMetadata: { mime: mimeType },\n                        });\n                        console.log(`Saved attachment ${filename} to ${key}.`);\n                    }\n\n                    const res = respond(ctx.message);\n                    res.addMessage({\n                        contentType: \"text/plain\",\n                        data: `${ctx.parsed.attachments.length} attachments saved.`,\n                    });\n                    await ctx.message.reply(res);\n                },\n            });\n\n        await kit.process(message);\n    },\n};\n```\n\n\u003c/details\u003e\n\n### Cloudflare Queues\n\nLeverage Cloudflare Queues to get more CPU time (15 minutes instead of 30 seconds) to process emails.\n\nSee [examples/cloudflare-queues](examples/cloudflare-queues/).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eSee the example code\u003c/summary\u003e\n\n```ts\nimport { EmailKit, EmailRouter, SizeGuard, respond } from \"cloudflare-email\";\nimport { Backup } from \"cloudflare-email-backup\";\nimport { EmailQueue, EmailQueueMessage } from \"cloudflare-email-queue\";\n\nexport interface Env {\n    R2: R2Bucket;\n    D1: D1Database;\n    FIRST: Queue\u003cEmailQueueMessage\u003e;\n    SECOND: Queue\u003cEmailQueueMessage\u003e;\n}\n\nexport default {\n    // receive email, perform size check, and enqueue it to the coresponding queue\n    async email(message: ForwardableEmailMessage, env: Env): Promise\u003cvoid\u003e {\n        const router = new EmailRouter()\n            .match(/@first\\.csie\\.cool$/, new EmailQueue(env.FIRST))\n            .match(/@second\\.csie\\.cool$/, new EmailQueue(env.SECOND));\n\n        const kit = new EmailKit()\n            .use(new SizeGuard(10 * 1024 * 1024))\n            .use(\n                new Backup({\n                    bucket: env.R2,\n                    prefix: \"backup\",\n                    database: env.D1,\n                    table: \"emails\",\n                }),\n            )\n            .use(router);\n\n        await kit.process(message);\n    },\n    // checkout the queued messages and process them\n    async queue(batch: MessageBatch\u003cEmailQueueMessage\u003e, env: Env) {\n        const backup = new Backup({\n            bucket: env.R2,\n            prefix: \"backup\",\n            database: env.D1,\n            table: \"emails\",\n        });\n\n        // retrieve and re-construct the message\n        const retrieve = async (m: EmailQueueMessage) =\u003e {\n            const raw = await backup.retrieve(m.message_id, m.from, m.to);\n            if (!raw) {\n                throw new Error(\"Cannot retrieve message.\");\n            }\n            return EmailQueue.retrieve(m, raw);\n        };\n\n        if (batch.queue === \"kit-example-first-email-queue\") {\n            for (const m of batch.messages) {\n                const message = await retrieve(m.body);\n\n                await new EmailKit()\n                    .use(async (ctx) =\u003e {\n                        const reply = respond(ctx.message);\n                        reply.addMessage({\n                            contentType: \"text/plain\",\n                            data: \"Greeting from first domain.\",\n                        });\n                        await ctx.message.reply(reply);\n                    })\n                    .handle({ message });\n\n                m.ack();\n            }\n        } else if (batch.queue === \"kit-example-second-email-queue\") {\n            for (const m of batch.messages) {\n                const message = await retrieve(m.body);\n\n                await new EmailKit()\n                    .use(async (ctx) =\u003e {\n                        const reply = respond(ctx.message);\n                        reply.addMessage({\n                            contentType: \"text/plain\",\n                            data: \"Greeting from second domain.\",\n                        });\n\n                        await ctx.message.reply(reply);\n                    })\n                    .handle({ message });\n\n                m.ack();\n            }\n        }\n    },\n};\n```\n\n\u003c/details\u003e\n\n## Documentation\n\nDive into the full potential of the Cloudflare Email Kit by visiting the [Documentation](https://jacoblincool.github.io/cloudflare-email-kit).\n\n## Join the Community\n\nWe welcome your contributions! To collaborate, fork the repository and submit your pull requests.\n\n## Need Help?\n\nEncountered a snag? Have a suggestion? [Open an issue on the GitHub issues page](https://github.com/JacobLinCool/cloudflare-email-kit/issues/new).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacoblincool%2Fcloudflare-email-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacoblincool%2Fcloudflare-email-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacoblincool%2Fcloudflare-email-kit/lists"}