How it works:
- On app deploy, autoincrement Cloudflare KV value
- Cloudflare Worker adds a "version" cookie to every Response with the content from the KV
- Client checks cookies periodically to see if the "version" cookie has changed. If so, reload page
| // bump version in Cloudflare KV | |
| const fetch = require('node-fetch') | |
| const vars = require('./cloudflare.var.js') | |
| async function api(method, body) { | |
| let res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${vars.CLOUDFLARE_ACCOUNT_ID}/storage/kv/namespaces/${vars.CLOUDFLARE_VERSION_NAMESPACE_ID}/values/version`, { | |
| method, | |
| headers: { | |
| "X-Auth-Email": vars.CLOUDFLARE_AUTH_EMAIL, | |
| "X-Auth-Key": vars.CLOUDFLARE_AUTH_KEY, | |
| }, | |
| body, | |
| }) | |
| return await res.text() | |
| } | |
| ;(async () => await api(`PUT`, (+await api(`GET`) || 0) + 1))() |
| // watch version cookie and reload | |
| const cookies_to_object = cookies => cookies.split(';').reduce((a, c) => { | |
| let [k, v] = c.split('=') | |
| a[k.trim()] = v | |
| return a | |
| }, {}) | |
| let previous_cookie = document.cookie | |
| let { version } = cookies_to_object(document.cookie) | |
| setInterval(function() { | |
| if(previous_cookie == document.cookie) return | |
| if(cookies_to_object(document.cookie).version !== version) location.reload() | |
| previous_cookie = document.cookie | |
| }, 100) |
| // instead of .env | |
| module.exports = { | |
| "CLOUDFLARE_AUTH_KEY": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | |
| "CLOUDFLARE_AUTH_EMAIL": "xxxxx@xxxxx.com", | |
| "CLOUDFLARE_ACCOUNT_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | |
| "CLOUDFLARE_ZONE_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | |
| "CLOUDFLARE_VERSION_NAMESPACE_ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | |
| } |
| // cloudflare worker | |
| /* global CLOUDFLARE_NAMESPACE */ | |
| async function handleRequest(request) { | |
| try { | |
| let response = await fetch(request) | |
| let cookie = response.headers.get('Set-Cookie') | |
| response = new Response(response.body, response) | |
| const version = await CLOUDFLARE_NAMESPACE.get('version') | |
| response.headers.set("Set-Cookie", `${cookie ? cookie+'; ' : ''}version=${version};path=/`) | |
| return response | |
| } catch(e) { | |
| return new Response(e.toString()) | |
| } | |
| } | |
| addEventListener('fetch', event => { | |
| event.respondWith(handleRequest(event.request)) | |
| }) |