diff --git a/infra/cloudflare-workers/README.md b/infra/cloudflare-workers/README.md index ff59654c5..a416b690f 100644 --- a/infra/cloudflare-workers/README.md +++ b/infra/cloudflare-workers/README.md @@ -1 +1,28 @@ -Currently manually copy/paste/deploy via Cloudflare Portal UI. +# Cloudflare Workers + +## Deployment + +Currently, these workers are manually deployed via the Cloudflare Portal UI: + +1. Go to the [Cloudflare Workers dashboard](https://dash.cloudflare.com/) +2. Select the appropriate worker +3. Copy and paste the updated code from the respective `.js` file +4. Click "Save and Deploy" + +## filterlists-api-host-rewrite-worker.js + +This worker is deployed at `api.filterlists.com` and: + +- Proxies requests to the Azure App Service backend +- Handles CORS preflight (OPTIONS) requests +- Adds CORS headers to responses for allowed origins +- Enables Cloudflare edge caching for API responses + +### Allowed Origins + +- `https://filterlists.com` +- `https://next.filterlists.com` +- `http://localhost:3000` +- Azure Static Web Apps staging environments matching the pattern: `https://nice-water-05873140f-*.eastus2.5.azurestaticapps.net` + +**Important**: After updating this file, remember to manually deploy the changes via the Cloudflare Portal UI. diff --git a/infra/cloudflare-workers/filterlists-api-host-rewrite-worker.js b/infra/cloudflare-workers/filterlists-api-host-rewrite-worker.js index 28d593e0a..10ba3019a 100644 --- a/infra/cloudflare-workers/filterlists-api-host-rewrite-worker.js +++ b/infra/cloudflare-workers/filterlists-api-host-rewrite-worker.js @@ -5,6 +5,11 @@ export default { async fetch(request, _, __) { const url = new URL(request.url); + // Handle OPTIONS preflight requests for CORS + if (request.method === "OPTIONS") { + return handleOptions(request); + } + const azureHostname = "app-filterlists-directory-prod.azurewebsites.net"; const originUrl = `https://${azureHostname}${url.pathname}${url.search}`; const originRequest = new Request(originUrl, request); @@ -19,6 +24,73 @@ export default { }, }); - return response; + // Ensure CORS headers are present in the response + return addCorsHeaders(response, request); }, }; + +/** + * Handle OPTIONS preflight requests + */ +function handleOptions(request) { + const origin = request.headers.get("Origin"); + + // Allow CORS from filterlists.com domains and localhost + const allowedOrigins = [ + "https://filterlists.com", + "https://next.filterlists.com", + "http://localhost:3000", + ]; + + // Check if origin is allowed or matches staging pattern + const isAllowed = allowedOrigins.includes(origin) || + (origin && origin.startsWith("https://nice-water-05873140f-") && + origin.endsWith(".eastus2.5.azurestaticapps.net")); + + if (isAllowed) { + return new Response(null, { + status: 204, + headers: { + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Methods": "GET, OPTIONS", + "Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers") || "*", + "Access-Control-Max-Age": "86400", + }, + }); + } + + return new Response(null, { status: 403 }); +} + +/** + * Add CORS headers to response + */ +function addCorsHeaders(response, request) { + const origin = request.headers.get("Origin"); + + // Allow CORS from filterlists.com domains and localhost + const allowedOrigins = [ + "https://filterlists.com", + "https://next.filterlists.com", + "http://localhost:3000", + ]; + + // Check if origin is allowed or matches staging pattern + const isAllowed = allowedOrigins.includes(origin) || + (origin && origin.startsWith("https://nice-water-05873140f-") && + origin.endsWith(".eastus2.5.azurestaticapps.net")); + + if (!isAllowed) { + return response; + } + + // Clone the response so we can modify headers + const newResponse = new Response(response.body, response); + + // Set CORS headers + newResponse.headers.set("Access-Control-Allow-Origin", origin); + newResponse.headers.set("Access-Control-Allow-Methods", "GET, OPTIONS"); + newResponse.headers.set("Vary", "Origin"); + + return newResponse; +}