Skip to content

Instantly share code, notes, and snippets.

@typhonius
Last active August 26, 2020 00:47
Show Gist options
  • Save typhonius/e43590bd83935baff2151e2c0c732b69 to your computer and use it in GitHub Desktop.
Save typhonius/e43590bd83935baff2151e2c0c732b69 to your computer and use it in GitHub Desktop.
Purge selected files depending on publish or update webhook action with Ghost and Cloudflare
/* eslint-disable no-undef */
const url = require('url');
function generateSimpleResponse(httpCode, strMessage) {
return new Response(JSON.stringify({ result: strMessage }), {
status: httpCode,
headers: {
'Content-Type': 'application/json',
},
});
}
/**
* Purge the cache depending on whether this is a new publication or if this
* is an update to an existing article. Be explicit about the URLs that we
* want to clear from the cache to ensure that we only purge what is
* necessary.
*/
async function purgeCache(event, request) {
let files = [];
if (event === 'publish') {
console.log('Clearing cache for homepage and rss.');
files = [
'https://www.adammalone.net/',
'https://www.adammalone.net/rss/',
];
}
if (event === 'update') {
const reqBody = await readRequestBody(request);
const bodyJson = JSON.parse(reqBody);
const purgeUrl = bodyJson.post.current.url || 'https://www.adammalone.net';
console.log(`Clearing cache for ${purgeUrl}`);
files = [
purgeUrl,
];
}
apiPayload = {
files: files,
};
const apiUrl = 'https://api.cloudflare.com/client/v4/zones/' + CF_ZONE_ID + '/purge_cache';
const init = {
body: JSON.stringify(apiPayload),
method: 'POST',
headers: {
Authorization: 'Bearer ' + CF_AUTH_TOKEN,
},
};
const response = await fetch(apiUrl, init);
return await response.json();
}
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});
/**
* readRequestBody reads in the incoming request body
* Use await readRequestBody(..) in an async function to get the string
* @param {Request} request the incoming request to read from
*/
async function readRequestBody(request) {
const { headers } = request;
const contentType = headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return JSON.stringify(await request.json());
}
else if (contentType.includes('application/text')) {
return await request.text();
}
else if (contentType.includes('text/html')) {
return await request.text();
}
else if (contentType.includes('form')) {
const formData = await request.formData();
const body = {};
for (const entry of formData.entries()) {
body[entry[0]] = entry[1];
}
return JSON.stringify(body);
}
else {
const myBlob = await request.blob();
const objectURL = URL.createObjectURL(myBlob);
return objectURL;
}
}
/**
* Determines the type of cache clearing that needs to be done by the path that
* we hit the endpoint with. Use this to then purge the cache.
*/
async function handleRequest(request) {
const urlParts = url.parse(request.url);
if (urlParts.pathname === `/cf-purge/purge/publish/${WEBHOOK_USER}/${WEBHOOK_PASSWORD}/`) {
const responseData = await purgeCache('publish', request);
if (responseData.success) {
return generateSimpleResponse(200, responseData);
}
else {
return generateSimpleResponse(500, 'FAILED');
}
}
if (urlParts.pathname === `/cf-purge/purge/update/${WEBHOOK_USER}/${WEBHOOK_PASSWORD}/`) {
const responseData = await purgeCache('update', request);
if (responseData.success) {
return generateSimpleResponse(200, responseData);
}
else {
return generateSimpleResponse(500, 'FAILED');
}
}
return generateSimpleResponse(404, 'NOT FOUND');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment