From a3d639df6c215d3354b6f173958b64a9e78d4597 Mon Sep 17 00:00:00 2001 From: m5r Date: Sat, 20 Sep 2025 14:35:51 +0200 Subject: [PATCH] purge cache with DELETE method --- CLAUDE.md | 15 ++++++++++++++- package.json | 2 +- src/index.ts | 11 +++++++++++ test/index.spec.ts | 31 +++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 72475b5..da4d938 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,8 @@ This is a Cloudflare Workers project called "url-cleaner" built with TypeScript. ## API Usage +### Clean URL Endpoint + **Endpoint**: `GET /?url=` **Response**: Plain text containing the cleaned URL @@ -27,6 +29,16 @@ This is a Cloudflare Workers project called "url-cleaner" built with TypeScript. - `/?url=https://example.com?utm_source=test` → `https://example.com` - `/?url=https://youtube.com/watch?v=abc&feature=share` → `https://youtube.com/watch?v=abc` +### Cache Purge Endpoint + +**Endpoint**: `DELETE /?url=` +**Response**: Plain text confirming cache deletion + +**Examples**: + +- `DELETE /?url=https://example.com?utm_source=test` → `Cache entry deleted` (200) +- `DELETE /?url=https://nonexistent.com` → `Cache entry not found` (404) + ## Common Commands ### Development @@ -67,7 +79,8 @@ test/ - **Fragment Cleaning**: Removes tracking from URL fragments/hash parameters (`#utm_campaign=test`) - **Raw Rules Support**: Handles complex regex-based cleaning for advanced tracking patterns - **Loop Prevention**: Tracks visited URLs to prevent infinite redirect loops -- **Response Caching**: 1-hour cache for improved performance +- **Response Caching**: 1-hour cache using Cloudflare Cache API for improved performance +- **Cache Management**: DELETE endpoint for manual cache purging ### Rule System diff --git a/package.json b/package.json index 4cb2f1a..467269d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "deploy": "wrangler deploy", "dev": "wrangler dev", "start": "wrangler dev", - "test": "vitest", + "test": "vitest --run", "cf-typegen": "wrangler types", "format": "prettier --write .", "format:check": "prettier --check ." diff --git a/src/index.ts b/src/index.ts index cada73e..f04b641 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,17 @@ export default { const cache = caches.default; const cacheKey = new Request(`${url.origin}/cache/${encodeURIComponent(targetUrl)}`); + if (request.method === "DELETE") { + const deleted = await cache.delete(cacheKey); + return new Response(deleted ? "Cache entry deleted" : "Cache entry not found", { + status: deleted ? 200 : 404, + headers: { + "Content-Type": "text/plain", + "Access-Control-Allow-Origin": "*", + }, + }); + } + let response = await cache.match(cacheKey); if (response) { return response; diff --git a/test/index.spec.ts b/test/index.spec.ts index 7de2398..7cec597 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -157,4 +157,35 @@ describe("URL Cleaner worker", () => { expect(response.status).toBe(200); // Should return original URL, not error expect(await response.text()).toBe("not-a-valid-url"); }); + + it("deletes cache entry on DELETE request", async () => { + // First, make a GET request to cache the response + const testUrl = "https://tiktok.com/video?_t=tracking&_r=more&u_code=123&normal=keep&other=stay"; + const getResponse = await SELF.fetch(`https://example.com/?url=${encodeURIComponent(testUrl)}`); + expect(getResponse.status).toBe(200); + expect(await getResponse.text()).toBe("https://tiktok.com/video?normal=keep&other=stay"); + + // Then delete the cache entry + const deleteResponse = await SELF.fetch(`https://example.com/?url=${encodeURIComponent(testUrl)}`, { + method: "DELETE", + }); + expect(deleteResponse.status).toBe(200); + expect(await deleteResponse.text()).toBe("Cache entry deleted"); + + const subsequentDeleteResponse = await SELF.fetch(`https://example.com/?url=${encodeURIComponent(testUrl)}`, { + method: "DELETE", + }); + expect(subsequentDeleteResponse.status).toBe(404); + expect(await subsequentDeleteResponse.text()).toBe("Cache entry not found"); + }); + + it("returns 404 when deleting non-existent cache entry", async () => { + const testUrl = "https://nonexistent.com?param=value"; + + const deleteResponse = await SELF.fetch(`https://example.com/?url=${encodeURIComponent(testUrl)}`, { + method: "DELETE", + }); + expect(deleteResponse.status).toBe(404); + expect(await deleteResponse.text()).toBe("Cache entry not found"); + }); });