Skip to content

Commit 2c4dad9

Browse files
mabelsclaude
andcommitted
feat(cloud): add cf-storage-cors worker to handle CORS preflight for R2 storage
Adds a Cloudflare Worker that intercepts OPTIONS preflight requests and returns 200 with CORS headers, forwarding GET/PUT/HEAD directly to the fireproof-cloud R2 bucket binding. Adds deploy step to CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3fe36f4 commit 2c4dad9

File tree

11 files changed

+282
-48
lines changed

11 files changed

+282
-48
lines changed

.github/workflows/ci-core-cf-deploy.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ jobs:
2323

2424
- uses: ./actions/base
2525

26+
- name: deploy cloud/backend/cf-storage-cors
27+
working-directory: cloud/backend/cf-storage-cors
28+
env:
29+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
30+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
31+
run: |
32+
pnpm run wrangler:deploy
33+
2634
- name: deploy cloud/backend/cf-d1 - 1
2735
working-directory: cloud/backend/cf-d1
2836
env:

cloud/backend/base/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
},
3838
"dependencies": {
3939
"@adviser/cement": "^0.5.34",
40-
"@cloudflare/workers-types": "^4.20260316.1",
40+
"@cloudflare/workers-types": "^4.20260317.1",
4141
"@fireproof/cloud-base": "workspace:0.0.0",
4242
"@fireproof/core-base": "workspace:0.0.0",
4343
"@fireproof/core-gateways-cloud": "workspace:0.0.0",

cloud/backend/cf-d1/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
},
4141
"dependencies": {
4242
"@adviser/cement": "^0.5.34",
43-
"@cloudflare/workers-types": "^4.20260316.1",
43+
"@cloudflare/workers-types": "^4.20260317.1",
4444
"@fireproof/cloud-backend-base": "workspace:0.0.0",
4545
"@fireproof/cloud-base": "workspace:0.0.0",
4646
"@fireproof/core-protocols-cloud": "workspace:0.0.0",
@@ -58,7 +58,7 @@
5858
"drizzle-kit": "0.30.6",
5959
"tsx": "^4.21.0",
6060
"vitest": "^4.0.14",
61-
"wrangler": "^4.73.0",
61+
"wrangler": "^4.76.0",
6262
"zx": "^8.8.5"
6363
}
6464
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { R2Bucket } from "@cloudflare/workers-types";
2+
interface Env {
3+
BUCKET: R2Bucket;
4+
}
5+
6+
const CORS_HEADERS = {
7+
"Access-Control-Allow-Origin": "*",
8+
"Access-Control-Allow-Methods": "GET, PUT, HEAD",
9+
"Access-Control-Allow-Headers": "*",
10+
};
11+
12+
export default {
13+
async fetch(request: Request, env: Env): Promise<Response> {
14+
if (request.method === "OPTIONS") {
15+
return new Response(null, { status: 200, headers: CORS_HEADERS });
16+
}
17+
18+
// eslint-disable-next-line no-restricted-globals
19+
const url = new URL(request.url);
20+
const key = url.pathname.replace(/^\//, "");
21+
22+
if (request.method === "HEAD") {
23+
const obj = await env.BUCKET.head(key);
24+
if (!obj) return new Response(null, { status: 404, headers: CORS_HEADERS });
25+
return new Response(null, { status: 200, headers: { ...CORS_HEADERS, etag: obj.etag } });
26+
}
27+
28+
if (request.method === "GET") {
29+
const obj = await env.BUCKET.get(key);
30+
if (!obj) return new Response("Not Found", { status: 404, headers: CORS_HEADERS });
31+
return new Response(obj.body as never, {
32+
status: 200,
33+
headers: { ...CORS_HEADERS, etag: obj.etag, "content-type": obj.httpMetadata?.contentType ?? "application/octet-stream" },
34+
});
35+
}
36+
37+
if (request.method === "PUT") {
38+
await env.BUCKET.put(key, request.body as never);
39+
return new Response(null, { status: 200, headers: CORS_HEADERS });
40+
}
41+
42+
return new Response("Method Not Allowed", { status: 405, headers: CORS_HEADERS });
43+
},
44+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@fireproof/cloud-backend-cf-storage-cors",
3+
"version": "0.0.0",
4+
"description": "CORS gateway worker for Fireproof R2 storage.",
5+
"type": "module",
6+
"scripts": {
7+
"build": "core-cli tsc",
8+
"pack": "echo cloud need not to pack",
9+
"publish": "echo skip",
10+
"wrangler:deploy": "wrangler deploy -c ./wrangler.toml --env dev"
11+
},
12+
"keywords": [
13+
"ledger",
14+
"JSON",
15+
"document",
16+
"IPLD",
17+
"CID",
18+
"IPFS"
19+
],
20+
"contributors": [
21+
"J Chris Anderson",
22+
"Alan Shaw",
23+
"Travis Vachon",
24+
"Mikeal Rogers",
25+
"Meno Abels"
26+
],
27+
"author": "J Chris Anderson",
28+
"license": "AFL-2.0",
29+
"homepage": "https://use-fireproof.com",
30+
"repository": {
31+
"type": "git",
32+
"url": "git+https://github.com/fireproof-storage/fireproof.git"
33+
},
34+
"bugs": {
35+
"url": "https://github.com/fireproof-storage/fireproof/issues"
36+
},
37+
"dependencies": {
38+
"@cloudflare/workers-types": "^4.20260317.1"
39+
},
40+
"devDependencies": {
41+
"@fireproof/core-cli": "workspace:0.0.0",
42+
"wrangler": "^4.76.0"
43+
}
44+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "./dist"
5+
}
6+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name = "fireproof-storage-cors"
2+
main = "index.ts"
3+
compatibility_date = "2025-02-24"
4+
5+
[observability]
6+
enabled = true
7+
head_sampling_rate = 1
8+
9+
# R2 binding points to the existing fireproof-cloud bucket
10+
# The custom domain storage.fireproof.direct must be re-pointed
11+
# from direct R2 access to this worker in the Cloudflare dashboard.
12+
[[r2_buckets]]
13+
binding = "BUCKET"
14+
bucket_name = "fireproof-cloud"
15+
16+
[env.dev]
17+
[[env.dev.r2_buckets]]
18+
binding = "BUCKET"
19+
bucket_name = "fireproof-cloud"
20+
21+
[[env.dev.routes]]
22+
pattern = "storage.fireproof.direct/*"
23+
zone_name = "fireproof.direct"

dashboard/backend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"zod": "^4.3.6"
3737
},
3838
"devDependencies": {
39-
"@cloudflare/workers-types": "^4.20260316.1",
39+
"@cloudflare/workers-types": "^4.20260317.1",
4040
"@eslint/js": "^9.39.4",
4141
"@fireproof/core-cli": "workspace:0.0.0",
4242
"@fireproof/core-keybag": "workspace:0.0.0",
@@ -47,7 +47,7 @@
4747
"eslint": "^9.39.2",
4848
"prettier": "^3.8.1",
4949
"vitest": "^4.0.8",
50-
"wrangler": "^4.73.0",
50+
"wrangler": "^4.76.0",
5151
"zx": "^8.8.5"
5252
}
5353
}

dashboard/frontend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
},
5858
"devDependencies": {
5959
"@cloudflare/vite-plugin": "^1.28.0",
60-
"@cloudflare/workers-types": "^4.20260316.1",
60+
"@cloudflare/workers-types": "^4.20260317.1",
6161
"@eslint/js": "^9.39.4",
6262
"@fireproof/core-cli": "workspace:0.0.0",
6363
"@libsql/client": "^0.17.0",
@@ -82,7 +82,7 @@
8282
"typescript": "^5.9.3",
8383
"vite": "^8.0.0",
8484
"vitest": "^4.0.8",
85-
"wrangler": "^4.73.0",
85+
"wrangler": "^4.76.0",
8686
"zx": "^8.8.5"
8787
}
8888
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"typescript": "5.9.3",
7878
"typescript-eslint": "^8.57.0",
7979
"vitest": "^4.0.14",
80-
"wrangler": "^4.73.0"
80+
"wrangler": "^4.76.0"
8181
},
8282
"repository": {
8383
"type": "git",

0 commit comments

Comments
 (0)