Created
January 18, 2023 10:52
-
-
Save arash16/947d44c8a8f5d7f9ea3532a93181b28f to your computer and use it in GitHub Desktop.
This file contains hidden or 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
SENDGRID_KEY=sendgrid-api-key | |
SECRET=secret-to-sign-subscription-links |
This file contains hidden or 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
import createSendgridClient from '../sendgrid'; | |
import { hash } from '../utils'; | |
interface Env { | |
SECRET: string; | |
SENDGRID_KEY: string; | |
} | |
export const onRequestPost: PagesFunction<Env> = async context => { | |
const { email } = await context.request.json<{ email?: string }>(); | |
// TODO: validate captcha | |
const secret = context.env.SECRET; | |
const hashed = await hash(email, secret); | |
const verifyLink = `https://website/api/verify?email=${encodeURIComponent( | |
email, | |
)}&hash=${hashed}`; | |
const sg = createSendgridClient(context.env.SENDGRID_KEY); | |
await sg.sendMail({ | |
to: email, | |
from: '[email protected]', // Use the email address or domain you verified above | |
subject: 'Confirm email subscription', | |
text: `Please confirm your subscription by opening following link: | |
${verifyLink}`, | |
html: `<p>Please confirm your subscription by opening following link:</p> | |
<p><a href="${verifyLink}">Verify</a></p>`, | |
}); | |
return Response.json({ email }, { status: 200 }); | |
}; |
This file contains hidden or 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
import createSendgridClient from '../sendgrid'; | |
import { hash } from '../utils'; | |
interface Env { | |
SECRET: string; | |
SENDGRID_KEY: string; | |
} | |
export const onRequestPost: PagesFunction<Env> = async context => { | |
const url = new URL(context.request.url); | |
const email = url.searchParams.get('email'); | |
const hashed = url.searchParams.get('email'); | |
const secret = context.env.SECRET; | |
if (hashed !== (await hash(email, secret))) { | |
return Response.redirect('/subscription?error=invalid-hash', 302); | |
} | |
const sg = createSendgridClient(context.env.SENDGRID_KEY); | |
await sg.addContact(['881a7cf5-c79e-495a-804e-e1b42e91cc3b'], email); | |
return Response.redirect('/subscription?email=' + email, 302); | |
}; |
This file contains hidden or 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
interface EmailOptions { | |
to: string; | |
from: string; | |
subject: string; | |
text: string; | |
html: string; | |
} | |
const createSendgridClient = (key: string) => { | |
const call = (url: string, options: RequestInit<RequestInitCfProperties>) => | |
fetch('https://api.sendgrid.com' + url, { | |
...options, | |
headers: { | |
...options.headers, | |
'Authorization': `Bearer ${key}`, | |
'Content-Type': 'application/json', | |
}, | |
}); | |
return { | |
sendMail(opts: EmailOptions) { | |
return call('/v3/mail/send', { | |
method: 'POST', | |
body: JSON.stringify({ | |
from: { name: 'Arash16', email: opts.from }, | |
personalizations: [{ to: [{ email: opts.to }] }], | |
subject: opts.subject, | |
content: [ | |
{ | |
type: 'text/html', | |
value: opts.html, | |
}, | |
], | |
// 'template_id': '', | |
}), | |
}); | |
}, | |
addContact(lists: string[], email: string, data?: Record<string, string>) { | |
return call('/v3/marketing/contacts', { | |
method: 'PUT', | |
body: JSON.stringify({ | |
list_ids: lists, | |
contacts: [ | |
{ | |
email, | |
custom_fields: data, | |
}, | |
], | |
}), | |
}).then(x => x.json()); | |
}, | |
removeContact(contactID: string) { | |
return call('/v3/marketing/contacts?ids=' + contactID, { | |
method: 'DELETE', | |
}).then(x => x.json()); | |
}, | |
}; | |
}; | |
export default createSendgridClient; |
This file contains hidden or 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
{ | |
"include": ["./**/*"], | |
"compilerOptions": { | |
"target": "esnext", | |
"module": "esnext", | |
"allowSyntheticDefaultImports": true, | |
"moduleResolution": "Node", | |
"lib": ["esnext"], | |
"types": ["@cloudflare/workers-types"] | |
} | |
} |
This file contains hidden or 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
export async function hash(...args: string[]) { | |
const str = args.join(':'); | |
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); | |
return [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join(''); | |
} |
This file contains hidden or 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
{ | |
"version": 1, | |
"include": ["/api/*"], | |
"exclude": [] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment