mirror of
https://github.com/misenhower/splatoon3.ink.git
synced 2026-10-04 04:46:43 -05:00
73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
import { setTimeout as sleep } from 'node:timers/promises';
|
|
|
|
export default class CloudflareCachePurger
|
|
{
|
|
constructor({ zoneId, apiToken, hosts, fetch: fetcher = globalThis.fetch, wait = sleep }) {
|
|
if (!/^[a-f0-9]{32}$/.test(zoneId ?? '') || !apiToken || !hosts?.length) {
|
|
throw new Error('Cache purging requires a zone ID, API token, and public hosts.');
|
|
}
|
|
|
|
if (hosts.some(host => !/^[a-z0-9.-]+$/.test(host)) || hosts.length > 100) {
|
|
throw new Error('Cache purge hosts must be hostnames without schemes or paths (at most 100).');
|
|
}
|
|
|
|
this.zoneId = zoneId;
|
|
this.apiToken = apiToken;
|
|
this.hosts = [...new Set(hosts)];
|
|
this.fetch = fetcher;
|
|
this.wait = wait;
|
|
}
|
|
|
|
async purgeData() {
|
|
// Prefix purges cover query-string variants and also recover a previous failed
|
|
// purge when the next sync has no changed objects. Images remain cached.
|
|
const body = JSON.stringify({
|
|
prefixes: this.hosts.map(host => `${host}/data/`),
|
|
});
|
|
|
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
let retryDelay = 1000 * 2 ** attempt;
|
|
let failure;
|
|
|
|
try {
|
|
const response = await this.fetch(
|
|
`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${this.apiToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body,
|
|
signal: AbortSignal.timeout(10_000),
|
|
},
|
|
);
|
|
const result = await response.json();
|
|
|
|
if (response.ok && result.success === true) return;
|
|
|
|
failure = new Error(`Cloudflare cache purge failed (HTTP ${response.status}).`);
|
|
|
|
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
throw Object.assign(failure, { permanent: true });
|
|
}
|
|
|
|
const retryAfter = Number(response.headers.get('Retry-After'));
|
|
|
|
if (Number.isFinite(retryAfter) && retryAfter > 0) {
|
|
retryDelay = Math.min(retryAfter * 1000, 30_000);
|
|
}
|
|
} catch (error) {
|
|
if (error.permanent) throw error;
|
|
|
|
// Do not expose response bodies or request credentials in logs.
|
|
failure = new Error('Cloudflare cache purge request failed.');
|
|
}
|
|
|
|
if (attempt === 2) throw failure;
|
|
|
|
await this.wait(retryDelay);
|
|
}
|
|
}
|
|
}
|