Created
February 23, 2020 23:58
-
-
Save justsml/04083707a30f34859d126b2e98e74967 to your computer and use it in GitHub Desktop.
Uses Cloudflare KV data store & Google ReCaptcha!
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
// Note to self: Currently running here: https://dash.cloudflare.com/eb29900091c43bef22edfb71df934698/workers/edit/fanclub | |
/* | |
## Utility Functions | |
*/ | |
const isEmailShaped = email => email && email.length > 5 && email.indexOf('@') > -1 | |
const sendJSON = (data, status = 200) => new Response( | |
typeof data !== 'string' ? JSON.stringify(data) : data, { | |
headers: { 'Content-Type': 'application/json; charset=UTF-8' }, | |
status | |
}) | |
/* | |
## Data Methods | |
*/ | |
const usersDB = { | |
async find(key) { | |
const result = await FANCLUB.get(key) | |
return result && result.length >= 1 ? JSON.parse(result) : result | |
}, | |
async create({name, email, ip, originalReferer, referer}) { | |
return FANCLUB.put(email, JSON.stringify({name, email, ip, originalReferer, referer, verified: false})) | |
}, | |
async list({ limit = 100 } = {}) { | |
const results = await FANCLUB.list({ limit }) | |
return (results && results.keys).map(o => o.name) | |
} | |
} | |
/* | |
## Request Handlers | |
*/ | |
const handlers = { | |
async listUsers(request) { | |
const keyList = await usersDB.list({ limit: 100 }) | |
return sendJSON(keyList) | |
}, | |
async signupUser(request) { | |
try { | |
const secret = await CONFIG.get('RECAPTCHA_SECRET') | |
const ip = request.headers.get('CF-Connecting-IP') | |
const body = JSON.parse(await request.text()) | |
const {name, email, originalReferer, referer} = body | |
const {recaptcha} = body | |
if (!isEmailShaped(email)) throw new Error('Invalid email agument') | |
if (! (await verifyReCaptcha({response: recaptcha, remoteip: ip, secret}))) throw new Error('Verify Captcha & try again') | |
if (await usersDB.find(email)) throw new Error('Email already registered!') | |
await usersDB.create({name, email, ip, originalReferer, referer, verified: false}) | |
return sendJSON({message: `Successfully registered ${name}`}) | |
} catch (err) { | |
return sendJSON({message: err.message, stack: err.stack}, 500) | |
} | |
} | |
} | |
/* | |
## ReCaptcha Dependency | |
More info: https://developers.google.com/recaptcha/docs/verify#api_request | |
*/ | |
async function verifyReCaptcha({response, remoteip, secret}) { | |
if (!secret) throw new Error('Admin Error: Please configure required server variable `RECAPTCHA_SECRET`.') | |
if (!response) throw new Error('API Error: Missing parameter `recaptcha` or `response`.') | |
return fetch(`https://www.google.com/recaptcha/api/siteverify`, { | |
method: 'POST', | |
body: new URLSearchParams({secret, response, remoteip}).toString() | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
console.info('recaptcha response:', data) | |
if (!data || !data.success) return false | |
return {message: `successfully verified captcha`, ...data, success: data.success} | |
}) | |
} | |
/* | |
## Cloudflare Tie-in | |
*/ | |
async function handleRequest(request) { | |
if (typeof CONFIG === 'undefined' || typeof FANCLUB === 'undefined') return sendJSON({message: 'System Error: Key-value stores not configured.'}) | |
if (request.method === 'GET') { | |
console.error('WARNING: DATA LEAK HERE!!!! REMOVE IN PROD!') | |
return handlers.listUsers(request) | |
} else if (request.method === 'POST') { | |
return handlers.signupUser(request) | |
} else { | |
return sendJSON({message: 'The specified http path & method not supported.'}, 400) | |
} | |
} | |
addEventListener('fetch', event => { | |
event.respondWith(handleRequest(event.request)) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment