Skip to content

Commit 789e208

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 2e52e57 commit 789e208

File tree

5 files changed

+125
-0
lines changed

5 files changed

+125
-0
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:
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
interface Env {
2+
BUCKET: R2Bucket;
3+
}
4+
5+
const CORS_HEADERS = {
6+
"Access-Control-Allow-Origin": "*",
7+
"Access-Control-Allow-Methods": "GET, PUT, HEAD",
8+
"Access-Control-Allow-Headers": "*",
9+
};
10+
11+
export default {
12+
async fetch(request: Request, env: Env): Promise<Response> {
13+
if (request.method === "OPTIONS") {
14+
return new Response(null, { status: 200, headers: CORS_HEADERS });
15+
}
16+
17+
const url = new URL(request.url);
18+
const key = url.pathname.replace(/^\//, "");
19+
20+
if (request.method === "HEAD") {
21+
const obj = await env.BUCKET.head(key);
22+
if (!obj) return new Response(null, { status: 404, headers: CORS_HEADERS });
23+
return new Response(null, { status: 200, headers: { ...CORS_HEADERS, etag: obj.etag } });
24+
}
25+
26+
if (request.method === "GET") {
27+
const obj = await env.BUCKET.get(key);
28+
if (!obj) return new Response("Not Found", { status: 404, headers: CORS_HEADERS });
29+
return new Response(obj.body, {
30+
status: 200,
31+
headers: { ...CORS_HEADERS, etag: obj.etag, "content-type": obj.httpMetadata?.contentType ?? "application/octet-stream" },
32+
});
33+
}
34+
35+
if (request.method === "PUT") {
36+
await env.BUCKET.put(key, request.body);
37+
return new Response(null, { status: 200, headers: CORS_HEADERS });
38+
}
39+
40+
return new Response("Method Not Allowed", { status: 405, headers: CORS_HEADERS });
41+
},
42+
};
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.20260316.1"
39+
},
40+
"devDependencies": {
41+
"@fireproof/core-cli": "workspace:0.0.0",
42+
"wrangler": "^4.73.0"
43+
}
44+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "./dist",
5+
"allowSyntheticDefaultImports": true,
6+
"types": ["@cloudflare/workers-types"]
7+
}
8+
}
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"

0 commit comments

Comments
 (0)