-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Add Cloudflare workers types for web-smtp-relay-client
- Loading branch information
Showing
8 changed files
with
298 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,69 @@ client.sendEmail(message) | |
.catch((error) => console.error('Error sending email:', error)); | ||
``` | ||
|
||
## Cloudflare Pages client and serverless functions | ||
|
||
### Client-side (Single Page Application) | ||
|
||
```typescript | ||
import { sendEmailWithCaptcha, EmailMessage } from '@sctg/web-smtp-relay-client'; | ||
|
||
const message: EmailMessage = { | ||
subject: 'Test Subject', | ||
body: 'This is a test email', | ||
destinations: ['[email protected]'] | ||
}; | ||
|
||
const captchaToken = 'hCaptcha-token-from-client'; | ||
|
||
sendEmailWithCaptcha(message, captchaToken) | ||
.then((success) => { | ||
if (success) { | ||
console.log('Email sent successfully'); | ||
} else { | ||
console.error('Failed to send email'); | ||
} | ||
}) | ||
.catch((error) => console.error('Error:', error)); | ||
``` | ||
|
||
### Server-side (Cloudflare Pages Serverless Function) | ||
|
||
```typescript | ||
import { cfSendEmailWithCaptcha, EmailMessage, WebSMTPRelayConfig } from '.'; | ||
import { PagesFunction, Response, EventContext } from '@cloudflare/workers-types'; | ||
interface Env { | ||
HCAPTCHA_SECRET: string; | ||
WEB_SMTP_RELAY_SCHEME: string; | ||
WEB_SMTP_RELAY_HOST: string; | ||
WEB_SMTP_RELAY_PORT: number; | ||
WEB_SMTP_RELAY_USERNAME: string; | ||
WEB_SMTP_RELAY_PASSWORD: string; | ||
} | ||
export const onRequestPost: PagesFunction<Env> = async (context) => { | ||
const { message, captcha } = await context.request.json() as { message: EmailMessage, captcha: string }; | ||
const config: WebSMTPRelayConfig = { | ||
scheme: 'https', | ||
host: 'relay.example.com', | ||
port: 443, | ||
username: 'admin', | ||
password: 'admin123' | ||
}; | ||
const result = await cfSendEmailWithCaptcha(message, captcha, context.env.HCAPTCHA_SECRET, config); | ||
|
||
return new Response(result, { | ||
headers: { 'Content-Type': 'application/json' }, | ||
}); | ||
} | ||
``` | ||
|
||
Make sure to set the following environment variables for the server-side function: | ||
|
||
- `HCAPTCHA_SECRET`: Your hCaptcha secret key | ||
- `WEB_SMTP_RELAY_HOST`: The URL of your web-smtp-relay service | ||
- `WEB_SMTP_RELAY_USERNAME`: Username for web-smtp-relay authentication | ||
- `WEB_SMTP_RELAY_PASSWORD`: Password for web-smtp-relay authentication | ||
|
||
## License | ||
|
||
This project is licensed under the GNU Affero General Public License v3.0 (AGPLv3). |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,44 @@ | ||
{ | ||
"name": "@sctg/web-smtp-relay-client", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "A simple client for the web-smtp-relay server", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/sctg-development/web-smtp-relay.git" | ||
}, | ||
"type": "git", | ||
"url": "git+https://github.com/sctg-development/web-smtp-relay.git" | ||
}, | ||
"private": false, | ||
"licenses": [ | ||
{ | ||
"type": "AGPL-3.0", | ||
"url": "https://www.gnu.org/licenses/agpl-3.0.html" | ||
} | ||
{ | ||
"type": "AGPL-3.0", | ||
"url": "https://www.gnu.org/licenses/agpl-3.0.html" | ||
} | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
"access": "public" | ||
}, | ||
"tag": "latest", | ||
"scripts": { | ||
"build": "tsc", | ||
"build": "tsc", | ||
"test": "echo \"Error: no test specified\" && exit 0", | ||
"prepublishOnly": "npm run build" | ||
"prepublishOnly": "npm run build" | ||
}, | ||
"keywords": ["smtp", "relay", "client"], | ||
"keywords": [ | ||
"smtp", | ||
"relay", | ||
"client" | ||
], | ||
"author": "Ronan LE MEILLAT", | ||
"license": "AGPL-3.0", | ||
"devDependencies": { | ||
"typescript": "^5.5.4", | ||
"@types/node": "^20", | ||
"@babel/parser": "^7.25.4", | ||
"@babel/types":"^7.25.4" | ||
"@babel/parser": "^7.25.4", | ||
"@babel/types": "^7.25.4", | ||
"@cloudflare/workers-types": "^4.20240821.1", | ||
"@types/node": "^20", | ||
"typescript": "^5.5.4" | ||
}, | ||
"files": [ | ||
"dist/**/*" | ||
"dist/**/*" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/** | ||
* Copyright (C) 2024 Ronan LE MEILLAT | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
import { EmailMessage } from "."; | ||
|
||
/** | ||
* Sends an email to the Cloudflare Page serverless function with a captcha. | ||
* | ||
* @param message - The email message to send. | ||
* @param captcha - The captcha string. | ||
* @returns A promise that resolves to a boolean indicating whether the email was sent successfully. | ||
*/ | ||
export async function sendEmailWithCaptcha(message: EmailMessage, captcha: string): Promise<boolean> { | ||
try { | ||
const response = await fetch('/api/send-email', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ message, captcha }), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to send email'); | ||
} | ||
|
||
const result = await response.json(); | ||
return result.error === null; | ||
} catch (error) { | ||
console.error('Error sending email:', error); | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/** | ||
* Copyright (C) 2024 Ronan LE MEILLAT | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
import { EmailMessage, WebSMTPRelayConfig } from '.'; | ||
|
||
const HCAPTCHA_VERIFY_URL = 'https://api.hcaptcha.com/siteverify'; | ||
type HCaptchaVerifyError = string | string[] | ||
|
||
export type HCaptchaVerifyResponse = { | ||
/** Is the passcode valid, and does it meet security criteria you specified, e.g. sitekey? */ | ||
success: boolean | ||
/** Timestamp of the challenge (ISO format yyyy-MM-dd'T'HH:mm:ssZZ) */ | ||
challenge_ts: string | ||
/** The hostname of the site where the challenge was solved */ | ||
hostname: string | ||
/** Optional: whether the response will be credited */ | ||
credit?: boolean | ||
/** Optional: any error codes */ | ||
'error-codes'?: HCaptchaVerifyError | ||
/** ENTERPRISE feature: a score denoting malicious activity */ | ||
score?: number | ||
/** ENTERPRISE feature: reason(s) for score */ | ||
score_reason?: string[] | ||
} | ||
|
||
/** | ||
* Sends an email with captcha verification using the web-smtp-relay client. | ||
* this function is intended to be used in a Cloudflare Pages serverless function. | ||
* the web-smtp-relay client is a simple client wrote in Go for sending emails through a web SMTP relay. | ||
* see https://github.com/sctg-development/web-smtp-relay | ||
* | ||
* @param message - The email message to send. | ||
* @param captcha - The captcha response string. | ||
* @param config - The configuration for the web-smtp-relay client. | ||
* @param hCaptchaSecret - The secret key for hCaptcha verification. | ||
* @returns A promise that resolves to a string indicating the result of the email sending operation. | ||
*/ | ||
export async function cfSendEmailWithCaptcha(message: EmailMessage, captcha: string, hCaptchaSecret:string, config:WebSMTPRelayConfig): Promise<string> { | ||
try { | ||
// Verify hCaptcha | ||
const hCaptchaResponse = await fetch(HCAPTCHA_VERIFY_URL, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: `response=${captcha}&secret=${hCaptchaSecret}`, | ||
}); | ||
|
||
const hCaptchaResult = await hCaptchaResponse.json() as HCaptchaVerifyResponse; | ||
|
||
if (!hCaptchaResult.success) { | ||
return JSON.stringify({ error: 'Invalid captcha' }); | ||
} | ||
|
||
// Send email via web-smtp-relay | ||
const relayHost = `${config.scheme}://${config.host}:${config.port}`; | ||
const relayUsername = config.username; | ||
const relayPassword = config.password; | ||
|
||
const auth = Buffer.from(`${relayUsername}:${relayPassword}`).toString('base64'); | ||
|
||
const relayResponse = await fetch(`${relayHost}/send`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Authorization': `Basic ${auth}`, | ||
}, | ||
body: JSON.stringify(message), | ||
}); | ||
|
||
if (!relayResponse.ok) { | ||
throw new Error('Failed to send email through relay'); | ||
} | ||
|
||
return JSON.stringify({ error: null }); | ||
} catch (error) { | ||
console.error('Error in sendEmailWithCaptcha:', error); | ||
return JSON.stringify({ error: 'Failed to send email' }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { cfSendEmailWithCaptcha, EmailMessage, WebSMTPRelayConfig } from '.'; | ||
import { PagesFunction, Response, EventContext } from '@cloudflare/workers-types'; | ||
interface Env { | ||
HCAPTCHA_SECRET: string; | ||
WEB_SMTP_RELAY_SCHEME: string; | ||
WEB_SMTP_RELAY_HOST: string; | ||
WEB_SMTP_RELAY_PORT: number; | ||
WEB_SMTP_RELAY_USERNAME: string; | ||
WEB_SMTP_RELAY_PASSWORD: string; | ||
} | ||
export const onRequestPost: PagesFunction<Env> = async (context) => { | ||
const { message, captcha } = await context.request.json() as { message: EmailMessage, captcha: string }; | ||
const config: WebSMTPRelayConfig = { | ||
scheme: 'https', | ||
host: 'relay.example.com', | ||
port: 443, | ||
username: 'admin', | ||
password: 'admin123' | ||
}; | ||
const result = await cfSendEmailWithCaptcha(message, captcha, context.env.HCAPTCHA_SECRET, config); | ||
|
||
return new Response(result, { | ||
headers: { 'Content-Type': 'application/json' }, | ||
}); | ||
} |
Oops, something went wrong.