Files
gitbook/src/lib/cache/cloudflare.ts
T
Samy Pessé 5f7b29bbc8 Improve purging of cache and return keys (#111)
* Use input url instead of request url

* Improve cache purging

* Lint

* Fix TS

* Add method to delete from cloudflare

* Clear on all backends

* Fix

* Add global cloudflare tag
2024-01-23 18:21:46 +01:00

93 lines
2.5 KiB
TypeScript

import { Buffer } from 'node:buffer';
import type { CacheStorage, Cache, Response as WorkerResponse } from '@cloudflare/workers-types';
import { CacheBackend, CacheEntry } from './types';
/**
* Cache implementation using the Cloudflare Cache API.
* https://developers.cloudflare.com/workers/runtime-apis/cache/
*/
export const cloudflareCache: CacheBackend = {
name: 'cloudflare',
fallback: true,
async get(key, options) {
const cache = getCache();
if (!cache) {
return null;
}
const cacheKey = await serializeKey(key);
const response = await cache.match(cacheKey);
if (!response || options?.signal?.aborted) {
return null;
}
const entry = await deserializeEntry(response);
return entry;
},
async set(key, entry) {
const cache = getCache();
if (cache) {
const cacheKey = await serializeKey(key);
await cache.put(cacheKey, serializeEntry(entry));
}
},
async del(keys) {
const cache = getCache();
if (cache) {
await Promise.all(
keys.map(async (key) => {
const cacheKey = await serializeKey(key);
await cache.delete(cacheKey);
}),
);
}
},
async revalidateTags(tags) {
return {
keys: [],
metas: [],
};
},
};
function getCache(): Cache | null {
if (typeof caches === 'undefined') {
return null;
}
// @ts-ignore
return (caches as CacheStorage).default ?? null;
}
async function serializeKey(key: string): Promise<string> {
const digest = await crypto.subtle.digest(
{
name: 'SHA-256',
},
new TextEncoder().encode(key),
);
const hash = Buffer.from(digest).toString('base64');
return `gitbook://gitbook.com/${hash}`;
}
function serializeEntry(entry: CacheEntry): WorkerResponse {
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('Cache-Control', `public, max-age=${(entry.meta.expiresAt - Date.now()) / 1000}`);
headers.set('Cache-Tag', ['gitbook-open', ...entry.meta.tags].join(','));
// @ts-ignore
return new Response(JSON.stringify(entry), {
headers,
});
}
async function deserializeEntry(response: WorkerResponse): Promise<CacheEntry> {
const entry = (await response.json()) as CacheEntry;
return entry;
}