📬 Contact Messages
📬 Cosmy Contact Worker
This Cloudflare Worker handles contact requests sent via email or API and forwards them to Discord channels and a configured email address. It supports three types of requests:
- Contact Messages
- Bug Reports
- Account Deletion Requests
🚀 How It Works
The worker listens for incoming API requests at specific endpoints:
POST https://cosmy.app/api/v1/contact/messagePOST https://cosmy.app/api/v1/contact/bug-reportPOST https://cosmy.app/api/v1/contact/account-deletion
These endpoints accept JSON payloads and send an email to one of the following addresses:
contact@cosmy.appbug-report@cosmy.appaccount-deletion@cosmy.app
The "from" address of the email is set to website+<message|bug-report|account-deletion>@cosmy.app when composed via the API.
When an email is received (either composed via the API or sent directly to the email addresses), it is parsed and forwarded to the appropriate Discord channel and a configured email address.
🛠️ Usage
1. Install Dependencies
Run the following command to install the required dependencies:
yarn
2. Development
To start developing on the worker, first make sure to create a .dev.vars file in the root directory of the project and add the required environment variables (see Environment Variables section).
Then, run the following command to start the worker in development mode:
yarn dev
This will use Cloudflare's wrangler tool to run the worker locally. As this worker use the email binding that is not available locally, the --remote flag is used to run the worker in the Cloudflare environment.
3. Deployment
To deploy the worker to Cloudflare:
yarn deploy
This build the worker in the dist directory, upload the source maps to Sentry, and deploy the worker to Cloudflare.
4. Debugging
To debug the published worker, you can see the logs in real-time using:
yarn debug
This will show you the logs for the worker, including any errors or debug messages.
5. Update types
Following a change in environment variables, or bindings, you may need to update the types in types.d.ts:
yarn generate:types
6. Update JSON Schemas
If you make changes to the TypeScript DTOs in @cosmy/shared, you can regenerate the JSON schemas using:
yarn generate:schemas
🌍 Environment Variables
The worker relies on the following environment variables for configuration:
| Variable | Description |
|---|---|
FORWARD_EMAIL_ADRESS | Email address to forward incoming emails to. |
DISCORD_BOT_TOKEN | Discord bot token for API authentication. |
DISCORD_MESSAGE_CHANNEL_ID | Discord channel ID for general contact messages. |
DISCORD_BUG_REPORT_CHANNEL_ID | Discord channel ID for bug reports. |
DISCORD_ACCOUNT_DELETION_CHANNEL_ID | Discord channel ID for account deletion requests. |
SENTRY_DSN | Sentry DSN for error tracking. |
Local Development
To run the worker locally, create a .dev.vars file in the root directory of the project and add the following environment variables:
FORWARD_EMAIL_ADRESS=cosmy.solutions@gmail.com
DISCORD_BOT_TOKEN=<discord-bot-token>
DISCORD_MESSAGE_CHANNEL_ID=<discord-message-channel-id>
DISCORD_BUG_REPORT_CHANNEL_ID=<discord-bug-report-channel-id>
DISCORD_ACCOUNT_DELETION_CHANNEL_ID=<discord-account-deletion-channel-id>
SENTRY_DSN=<sentry-dsn>
Deployment
After the worker is deployed, you can set the environment variables to use in production. You can do this using the Cloudflare dashboard or the wrangler CLI.
wrangler secret put FORWARD_EMAIL_ADRESS # When prompted, enter the email address to forward incoming emails to.
wrangler secret put DISCORD_BOT_TOKEN # When prompted, enter the Discord bot token.
wrangler secret put DISCORD_MESSAGE_CHANNEL_ID # When prompted, enter the Discord channel ID for general contact messages.
wrangler secret put DISCORD_BUG_REPORT_CHANNEL_ID # When prompted, enter the Discord channel ID for bug reports.
wrangler secret put DISCORD_ACCOUNT_DELETION_CHANNEL_ID # When prompted, enter the Discord channel ID for account deletion requests.
wrangler secret put SENTRY_DSN # When prompted, enter the Sentry DSN.
📂 Project Structure
Here is an overview of the project's structure:
├─ 📁 src/
│ ├── 📄 config.ts # ⚙️ Form types and email settings
│ ├── 📄 email-handler.ts # 📬 Handles emails and forwards to Discord
│ ├── 📄 fetch-handler.ts # 🌐 Handles API requests
│ ├── 📄 helpers.ts # 🛠️ Utilities for validation and email handling
│ ├── 📄 index.ts # 🚀 Worker entry point
│ ├── 📄 schemas.json # 📝 JSON schemas for validation
│ └── 📄 schemas.ts # 🛡️ TypeScript definitions for schemas
├─ 📄 .dev.vars # 🌍 Local environment variables
├─ 📄 .gitignore # 🚫 Ignored files
├─ 📄 package.json # 📦 Dependencies and scripts
├─ 📄 README.md # 📖 Documentation
├─ 📄 tsconfig.json # ⚙️ TypeScript config
├─ 📄 tsconfig.schemas.json # ⚙️ Config for schema generation
├─ 📄 types.d.ts # 🛡️ Cloudflare types
├─ 📄 wrangler.jsonc # ☁️ Cloudflare Worker config
└─ 📄 yarn.lock # 🔒 Dependency lock file
📜 API Endpoints
The worker exposes the following API endpoints:
1. POST /api/v1/contact/message
Send a general contact message to contact@cosmy.app and forward it to the Discord contact channel.
Request Body
{
"name": "John Doe",
"email": "john.doe@example.com",
"message": "Hello, I have a question about your service."
}
2. POST /api/v1/contact/bug-report
Send a bug report to bug-report@cosmy.app and forward it to the Discord bug report channel.
Request Body
{
"name": "Jane Doe",
"email": "jane.doe@example.com",
"title": "App crashes on login",
"severity": "high",
"stepsToReproduce": "1. Open the app\n2. Enter credentials\n3. Press login",
"message": "The app crashes with a 500 error."
}
Attachments
You can include up to 10 attachments (max 1MB each) in the request.
Note: This endpoint supports attachments. You can include up to 10 attachments (max 1MB each) in the request. The payload should be a
multipart/form-datarequest.
3. POST /api/v1/contact/account-deletion
Send an account deletion request to account-deletion@cosmy.app and forward it to the Discord account deletion channel.
Request Body
{
"name": "John Doe",
"email": "john.doe@example.com"
}
📩 Email endpoints
The worker listens for incoming emails at the following addresses:
| Email Address | Description |
|---|---|
contact@cosmy.app | General contact messages. |
bug-report@cosmy.app | Bug reports. |
account-deletion@cosmy.app | Account deletion requests. |
For each email address, the worker will parse the incoming email and forward it to the appropriate Discord channel and to the email address specified in the environment variable FORWARD_EMAIL_ADRESS.
🛡️ Validation
The worker validates incoming requests using JSON schemas defined in the schemas.json file. If validation fails, the worker responds with a 400 Bad Request following the Cosmy response format.
For attachment validation, the errors look like this:
{
"status": "error",
"message": "Attachments are not allowed for this form",
"code": "CONTACT/ATTACHMENT_NOT_ALLOWED"
}
{
"status": "error",
"message": "The attachment is too large",
"code": "CONTACT/ATTACHMENT_TOO_LARGE",
"details": {
"fileName": "4mb.jpg",
"fileSize": 4200000,
"maxSize": 1048576
}
}
{
"status": "error",
"message": "The attachment limit has been exceeded",
"code": "CONTACT/ATTACHMENT_LIMIT_EXCEEDED",
"details": {
"fileCount": 11,
"maxFiles": 10
}
}
📦 Dependencies
The worker uses Yarn for dependency management. The dependencies are listed in the package.json file.
Main Dependencies
@cfworker/json-schema: For JSON schema validation.postal-mime: For parsing incoming emails.discord-api-types: For interacting with the Discord API.hono: For building the API endpoints.@hono/sentry: For Sentry integration.@cosmy/shared: For shared types and DTOs.
Development Dependencies
wrangler: For deploying and managing Cloudflare Workers.ts-json-schema-generator: For generating JSON schemas from the TypeScript DTO from@cosmy/shared.@sentry/cli: For managing Sentry source maps.eslint: For linting and code quality checks.