From c204010ee495262c1eca4499feba789acaa65ebb Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 2 Sep 2025 14:38:09 +0200 Subject: [PATCH] Log cache purge errors as errors --- .changeset/good-eyes-jog.md | 5 +++++ examples/e2e/app-router/open-next.config.ts | 3 ++- packages/cloudflare/src/api/cloudflare-context.ts | 6 +++--- .../cloudflare/src/api/overrides/cache-purge/index.ts | 3 ++- packages/cloudflare/src/api/overrides/internal.ts | 10 +++++----- 5 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 .changeset/good-eyes-jog.md diff --git a/.changeset/good-eyes-jog.md b/.changeset/good-eyes-jog.md new file mode 100644 index 000000000..d43348db1 --- /dev/null +++ b/.changeset/good-eyes-jog.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Log cache purge errors as errors diff --git a/examples/e2e/app-router/open-next.config.ts b/examples/e2e/app-router/open-next.config.ts index 2b25f7303..7d3346c1f 100644 --- a/examples/e2e/app-router/open-next.config.ts +++ b/examples/e2e/app-router/open-next.config.ts @@ -20,7 +20,8 @@ export default defineCloudflareConfig({ }, }, }), - cachePurge: purgeCache({ type: "durableObject" }), + // `CACHE_PURGE_ZONE_ID` and `CACHE_PURGE_API_TOKEN` are required to enable cache purge + // cachePurge: purgeCache({ type: "durableObject" }), enableCacheInterception: true, queue: queueCache(doQueue), }); diff --git a/packages/cloudflare/src/api/cloudflare-context.ts b/packages/cloudflare/src/api/cloudflare-context.ts index 6543a61ec..d10d79d30 100644 --- a/packages/cloudflare/src/api/cloudflare-context.ts +++ b/packages/cloudflare/src/api/cloudflare-context.ts @@ -56,10 +56,10 @@ declare global { // This can be safely used if you don't use an eventually consistent incremental cache (i.e. R2 without the regional cache for example) NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE?: string; - // Below are the optional env variables for purging the cache - // Durable Object namespace to use for the durable object cache purge + // Below are the env variables to use for purging the cache + // Durable Object namespace to use for the durable object cache purge (not needed in direct mode) NEXT_CACHE_DO_PURGE?: DurableObjectNamespace; - // The amount of time in seconds that the cache purge will wait before purging the cache + // The amount of time in seconds that the cache purge will wait before purging the cache (not needed in direct mode) NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS?: string; // The zone ID to use for the cache purge https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/ CACHE_PURGE_ZONE_ID?: string; diff --git a/packages/cloudflare/src/api/overrides/cache-purge/index.ts b/packages/cloudflare/src/api/overrides/cache-purge/index.ts index cf960122c..cde4206c4 100644 --- a/packages/cloudflare/src/api/overrides/cache-purge/index.ts +++ b/packages/cloudflare/src/api/overrides/cache-purge/index.ts @@ -1,3 +1,4 @@ +import { error } from "@opennextjs/aws/adapters/logger.js"; import type { CDNInvalidationHandler } from "@opennextjs/aws/types/overrides.js"; import { getCloudflareContext } from "../../cloudflare-context.js"; @@ -19,7 +20,7 @@ export const purgeCache = ({ type = "direct" }: PurgeOptions) => { } else { const durableObject = env.NEXT_CACHE_DO_PURGE; if (!durableObject) { - debugCache("cdnInvalidation", "No durable object found. Skipping cache purge."); + error("Purge cache: NEXT_CACHE_DO_PURGE not found. Skipping cache purge."); return; } const id = durableObject.idFromName("cache-purge"); diff --git a/packages/cloudflare/src/api/overrides/internal.ts b/packages/cloudflare/src/api/overrides/internal.ts index 0a57cca40..3e983fa5b 100644 --- a/packages/cloudflare/src/api/overrides/internal.ts +++ b/packages/cloudflare/src/api/overrides/internal.ts @@ -1,5 +1,6 @@ import { createHash } from "node:crypto"; +import { error } from "@opennextjs/aws/adapters/logger.js"; import type { CacheEntryType, CacheValue } from "@opennextjs/aws/types/overrides.js"; import { getCloudflareContext } from "../cloudflare-context.js"; @@ -50,7 +51,7 @@ export async function purgeCacheByTags(tags: string[]) { export async function internalPurgeCacheByTags(env: CloudflareEnv, tags: string[]) { if (!env.CACHE_PURGE_ZONE_ID && !env.CACHE_PURGE_API_TOKEN) { // THIS IS A NO-OP - debugCache("purgeCacheByTags", "No cache zone ID or API token provided. Skipping cache purge."); + error("No cache zone ID or API token provided. Skipping cache purge."); return "missing-credentials"; } @@ -71,7 +72,7 @@ export async function internalPurgeCacheByTags(env: CloudflareEnv, tags: string[ ); if (response.status === 429) { // Rate limit exceeded - debugCache("purgeCacheByTags", "Rate limit exceeded. Skipping cache purge."); + error("purgeCacheByTags: Rate limit exceeded. Skipping cache purge."); return "rate-limit-exceeded"; } const bodyResponse = (await response.json()) as { @@ -79,9 +80,8 @@ export async function internalPurgeCacheByTags(env: CloudflareEnv, tags: string[ errors: Array<{ code: number; message: string }>; }; if (!bodyResponse.success) { - debugCache( - "purgeCacheByTags", - "Cache purge failed. Errors:", + error( + "purgeCacheByTags: Cache purge failed. Errors:", bodyResponse.errors.map((error) => `${error.code}: ${error.message}`) ); return "purge-failed";