From 655af51766697df226ab5880c85b5d66e614cdde Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Tue, 31 Mar 2026 23:52:29 +0100 Subject: [PATCH] feat: tell cloudflare to purge cache after editing mii --- .env.example | 3 +++ src/app/api/mii/[id]/edit/route.ts | 34 +++++++++++++++++++++--------- src/app/mii/[id]/image/route.ts | 2 +- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 8911495..b69eb39 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,9 @@ REDIS_URL="redis://localhost:6379/0" # Used for metadata, sitemaps, etc. NEXT_PUBLIC_BASE_URL=http://localhost:3000 +CLOUDFLARE_ZONE_ID=XXXXXXXXXXXXXXXX +CLOUDFLARE_API_TOKEN=XXXXXXXXXXXXXXXX + # Used for error tracking NEXT_PUBLIC_SENTRY_DSN="" SENTRY_URL="" diff --git a/src/app/api/mii/[id]/edit/route.ts b/src/app/api/mii/[id]/edit/route.ts index 9f51e9d..13d79f2 100644 --- a/src/app/api/mii/[id]/edit/route.ts +++ b/src/app/api/mii/[id]/edit/route.ts @@ -123,7 +123,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< } // Prevent non-admins from quarantining Miis - if (quarantined && session.user?.id != process.env.NEXT_PUBLIC_ADMIN_USER_ID) return rateLimit.sendResponse({ error: `You're not an admin!` }, 401); + if (quarantined && session.user?.id?.toString() !== process.env.NEXT_PUBLIC_ADMIN_USER_ID) + return rateLimit.sendResponse({ error: `You're not an admin!` }, 401); // Edit Mii in database const updateData: Prisma.MiiUpdateInput = {}; @@ -136,7 +137,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< if (instructions !== undefined) updateData.instructions = instructions; if (images.length > 0) updateData.imageCount = images.length; - if (Object.keys(updateData).length == 0) return rateLimit.sendResponse({ error: "Nothing was changed" }, 400); + if (Object.keys(updateData).length === 0) return rateLimit.sendResponse({ error: "Nothing was changed" }, 400); const updatedMii = await prisma.mii.update({ where: { id: miiId, @@ -230,15 +231,28 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< } } - if (description === undefined) { - // If images or description were not changed, regenerate the metadata image - try { - await generateMetadataImage(updatedMii, updatedMii.user.name!); - } catch (error) { - console.error(error); - return rateLimit.sendResponse({ error: `Failed to generate 'metadata' type image for mii ${miiId}` }, 500); - } + try { + await generateMetadataImage(updatedMii, updatedMii.user.name!); + } catch (error) { + console.error(error); + return rateLimit.sendResponse({ error: `Failed to generate 'metadata' type image for mii ${miiId}` }, 500); } + // Tell Cloudflare to purge cache for the changed pages + fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.CLOUDFLARE_ZONE_ID}/purge_cache`, { + method: "POST", + headers: { Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json" }, + body: JSON.stringify({ + files: [ + `${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}`, + `${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=mii`, + `${process.env.NEXT_PUBLIC_BASE_URL}/mii/${miiId}/image?type=features`, + ], + }), + }).catch((err) => { + console.error("Cloudflare cache purge failed:", err); + Sentry.captureException(err, { extra: { stage: "cloudflare-purge", miiId } }); + }); + return rateLimit.sendResponse({ success: true }); } diff --git a/src/app/mii/[id]/image/route.ts b/src/app/mii/[id]/image/route.ts index 1dd6b05..6d23d95 100644 --- a/src/app/mii/[id]/image/route.ts +++ b/src/app/mii/[id]/image/route.ts @@ -19,7 +19,7 @@ const searchParamsSchema = z.object({ }); export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const rateLimit = new RateLimit(request, 20000, "/mii/image"); + const rateLimit = new RateLimit(request, 200, "/mii/image"); const check = await rateLimit.handle(); if (check) return check;