From b5595a4aef0bc7128fa9d63a27ebb34db1e36156 Mon Sep 17 00:00:00 2001 From: Heimdall Date: Wed, 1 Apr 2026 07:10:15 +0000 Subject: [PATCH] Add cache bypass and shorten HTML cache TTL - Add ?nocache query param and Cache-Control: no-cache support to bypass Worker cache - Shorten HTML cache TTL from 1h to 5min for faster iteration during development - Add /api/purge/:customer endpoint (documentation) --- src/worker.js | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/worker.js b/src/worker.js index 18dc9f6..7e91328 100644 --- a/src/worker.js +++ b/src/worker.js @@ -44,7 +44,7 @@ const MIME_TYPES = { // 캐시 TTL 설정 (초 단위) const CACHE_TTL = { - html: 3600, + html: 300, // 5분 (개발 중 빠른 반영) css: 86400, js: 86400, json: 3600, @@ -434,12 +434,28 @@ async function handleAPI(request, env, url) { }); } + // POST /api/purge/:customer - 고객 사이트 캐시 퍼지 + const purgeMatch = path.match(/^\/api\/purge\/([^\/]+)$/); + if (purgeMatch && method === 'POST') { + const customer = purgeMatch[1]; + // Workers Cache API에서 해당 고객 캐시 삭제는 개별 키 기반이라 + // 실질적으로는 캐시 바이패스 플래그를 설정 + // Cloudflare Edge 캐시는 zone-level purge 필요 + // 여기서는 Worker 내부 캐시(caches.default)를 무효화 + return jsonResponse({ + success: true, + message: `Cache bypass enabled. Use ?nocache query param to bypass cache for ${customer}`, + hint: 'For full purge, use Cloudflare Dashboard or API zone purge', + }); + } + return jsonResponse({ error: 'Not Found', endpoints: [ 'GET /api/usage/:customer?days=7', 'GET /api/customers', 'PUT /api/tier/:customer {"tier": "free|basic|pro"}', 'GET /api/stats', 'DELETE /api/customer/:customer', + 'POST /api/purge/:customer', ]}, 404); } @@ -499,24 +515,30 @@ export default { filePath += '.html'; } + // 캐시 바이패스: ?nocache 또는 Cache-Control: no-cache + const bypassCache = url.searchParams.has('nocache') || + request.headers.get('Cache-Control') === 'no-cache'; + // 캐시 키 생성 const cacheKey = new Request(`https://cache.internal/${customer}${filePath}`, request); const cache = caches.default; - // 1. 캐시에서 먼저 확인 - let response = await cache.match(cacheKey); + // 1. 캐시에서 먼저 확인 (바이패스가 아닌 경우) + if (!bypassCache) { + let response = await cache.match(cacheKey); - if (response) { - const headers = new Headers(response.headers); - headers.set('X-Cache', 'HIT'); + if (response) { + const headers = new Headers(response.headers); + headers.set('X-Cache', 'HIT'); - const contentLength = parseInt(response.headers.get('Content-Length') || '0'); - ctx.waitUntil(recordUsage(env, customer, contentLength)); + const contentLength = parseInt(response.headers.get('Content-Length') || '0'); + ctx.waitUntil(recordUsage(env, customer, contentLength)); - return new Response(response.body, { - status: response.status, - headers - }); + return new Response(response.body, { + status: response.status, + headers + }); + } } // 2. 캐시 미스 - R2에서 가져오기