Skip to content

Commit 91cc741

Browse files
committed
Added the ability to enable, disable, and get r2.dev public access URLs for R2 buckets.
1 parent 3ee1353 commit 91cc741

File tree

5 files changed

+369
-0
lines changed

5 files changed

+369
-0
lines changed

.changeset/thin-hairs-explain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Added the ability to enable, disable, and get r2.dev public access URLs for R2 buckets.

packages/wrangler/src/__tests__/r2.test.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { actionsForEventCategories } from "../r2/helpers";
55
import { endEventLoop } from "./helpers/end-event-loop";
66
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
77
import { mockConsoleMethods } from "./helpers/mock-console";
8+
import { mockConfirm } from "./helpers/mock-dialogs";
89
import { useMockIsTTY } from "./helpers/mock-istty";
910
import { createFetchResult, msw, mswR2handlers } from "./helpers/msw";
1011
import { runInTempDir } from "./helpers/run-in-tmp";
@@ -97,6 +98,7 @@ describe("r2", () => {
9798
wrangler r2 bucket delete <name> Delete an R2 bucket
9899
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
99100
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
101+
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
100102
101103
GLOBAL FLAGS
102104
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -131,6 +133,7 @@ describe("r2", () => {
131133
wrangler r2 bucket delete <name> Delete an R2 bucket
132134
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
133135
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
136+
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
134137
135138
GLOBAL FLAGS
136139
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -1467,6 +1470,139 @@ describe("r2", () => {
14671470
});
14681471
});
14691472
});
1473+
1474+
describe("dev-url", () => {
1475+
const { setIsTTY } = useMockIsTTY();
1476+
mockAccountId();
1477+
mockApiToken();
1478+
describe("get", () => {
1479+
it("should retrieve the r2.dev URL of a bucket when public access is enabled", async () => {
1480+
const bucketName = "my-bucket";
1481+
const domainInfo = {
1482+
bucketId: "bucket-id-123",
1483+
domain: "pub-bucket-id-123.r2.dev",
1484+
enabled: true,
1485+
};
1486+
msw.use(
1487+
http.get(
1488+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1489+
async ({ params }) => {
1490+
const { accountId, bucketName: bucketParam } = params;
1491+
expect(accountId).toEqual("some-account-id");
1492+
expect(bucketParam).toEqual(bucketName);
1493+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1494+
},
1495+
{ once: true }
1496+
)
1497+
);
1498+
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
1499+
expect(std.out).toMatchInlineSnapshot(`
1500+
"Public access is enabled at 'https://pub-bucket-id-123.r2.dev'."
1501+
`);
1502+
});
1503+
1504+
it("should show that public access is disabled when it is disabled", async () => {
1505+
const bucketName = "my-bucket";
1506+
const domainInfo = {
1507+
bucketId: "bucket-id-123",
1508+
domain: "pub-bucket-id-123.r2.dev",
1509+
enabled: false,
1510+
};
1511+
msw.use(
1512+
http.get(
1513+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1514+
async ({ params }) => {
1515+
const { accountId, bucketName: bucketParam } = params;
1516+
expect(accountId).toEqual("some-account-id");
1517+
expect(bucketParam).toEqual(bucketName);
1518+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1519+
},
1520+
{ once: true }
1521+
)
1522+
);
1523+
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
1524+
expect(std.out).toMatchInlineSnapshot(`
1525+
"Public access via the r2.dev URL is disabled."
1526+
`);
1527+
});
1528+
});
1529+
1530+
describe("enable", () => {
1531+
it("should enable public access", async () => {
1532+
const bucketName = "my-bucket";
1533+
const domainInfo = {
1534+
bucketId: "bucket-id-123",
1535+
domain: "pub-bucket-id-123.r2.dev",
1536+
enabled: true,
1537+
};
1538+
1539+
setIsTTY(true);
1540+
mockConfirm({
1541+
text:
1542+
`Are you sure you enable public access for bucket '${bucketName}'? ` +
1543+
`The contents of your bucket will be made publicly available at its r2.dev URL`,
1544+
result: true,
1545+
});
1546+
msw.use(
1547+
http.put(
1548+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1549+
async ({ request, params }) => {
1550+
const { accountId, bucketName: bucketParam } = params;
1551+
expect(accountId).toEqual("some-account-id");
1552+
expect(bucketParam).toEqual(bucketName);
1553+
const requestBody = await request.json();
1554+
expect(requestBody).toEqual({ enabled: true });
1555+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1556+
},
1557+
{ once: true }
1558+
)
1559+
);
1560+
await runWrangler(`r2 bucket dev-url enable ${bucketName}`);
1561+
expect(std.out).toMatchInlineSnapshot(`
1562+
"Enabling public access for bucket 'my-bucket'...
1563+
✨ Public access enabled at 'https://pub-bucket-id-123.r2.dev'."
1564+
`);
1565+
});
1566+
});
1567+
1568+
describe("disable", () => {
1569+
it("should disable public access", async () => {
1570+
const bucketName = "my-bucket";
1571+
const domainInfo = {
1572+
bucketId: "bucket-id-123",
1573+
domain: "pub-bucket-id-123.r2.dev",
1574+
enabled: false,
1575+
};
1576+
1577+
setIsTTY(true);
1578+
mockConfirm({
1579+
text:
1580+
`Are you sure you disable public access for bucket '${bucketName}'? ` +
1581+
`The contents of your bucket will no longer be publicly available at its r2.dev URL`,
1582+
result: true,
1583+
});
1584+
msw.use(
1585+
http.put(
1586+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1587+
async ({ request, params }) => {
1588+
const { accountId, bucketName: bucketParam } = params;
1589+
expect(accountId).toEqual("some-account-id");
1590+
expect(bucketParam).toEqual(bucketName);
1591+
const requestBody = await request.json();
1592+
expect(requestBody).toEqual({ enabled: false });
1593+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1594+
},
1595+
{ once: true }
1596+
)
1597+
);
1598+
await runWrangler(`r2 bucket dev-url disable ${bucketName}`);
1599+
expect(std.out).toMatchInlineSnapshot(`
1600+
"Disabling public access for bucket 'my-bucket'...
1601+
Public access disabled at 'https://pub-bucket-id-123.r2.dev'."
1602+
`);
1603+
});
1604+
});
1605+
});
14701606
});
14711607

14721608
describe("r2 object", () => {

packages/wrangler/src/r2/helpers.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,56 @@ export async function deleteEventNotificationConfig(
621621
}
622622
}
623623

624+
export interface R2DevDomainInfo {
625+
bucketId: string;
626+
domain: string;
627+
enabled: boolean;
628+
}
629+
630+
export async function getR2DevDomain(
631+
accountId: string,
632+
bucketName: string,
633+
jurisdiction?: string
634+
): Promise<R2DevDomainInfo> {
635+
const headers: HeadersInit = {};
636+
if (jurisdiction) {
637+
headers["cf-r2-jurisdiction"] = jurisdiction;
638+
}
639+
640+
const result = await fetchResult<R2DevDomainInfo>(
641+
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
642+
{
643+
method: "GET",
644+
headers,
645+
}
646+
);
647+
return result;
648+
}
649+
650+
export async function updateR2DevDomain(
651+
accountId: string,
652+
bucketName: string,
653+
enabled: boolean,
654+
jurisdiction?: string
655+
): Promise<R2DevDomainInfo> {
656+
const headers: HeadersInit = {
657+
"Content-Type": "application/json",
658+
};
659+
if (jurisdiction) {
660+
headers["cf-r2-jurisdiction"] = jurisdiction;
661+
}
662+
663+
const result = await fetchResult<R2DevDomainInfo>(
664+
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
665+
{
666+
method: "PUT",
667+
headers,
668+
body: JSON.stringify({ enabled }),
669+
}
670+
);
671+
return result;
672+
}
673+
624674
/**
625675
* R2 bucket names must only contain alphanumeric and - characters.
626676
*/

packages/wrangler/src/r2/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
usingLocalBucket,
2424
} from "./helpers";
2525
import * as Notification from "./notification";
26+
import * as PublicDevUrl from "./public-dev-url";
2627
import * as Sippy from "./sippy";
2728
import type { CommonYargsArgv, SubHelp } from "../yargs-types";
2829
import type { R2PutOptions } from "@cloudflare/workers-types/experimental";
@@ -607,6 +608,32 @@ export function r2(r2Yargs: CommonYargsArgv, subHelp: SubHelp) {
607608
);
608609
}
609610
);
611+
612+
r2BucketYargs.command(
613+
"dev-url",
614+
"Manage public access via the r2.dev URL for an R2 bucket",
615+
(domainYargs) => {
616+
return domainYargs
617+
.command(
618+
"enable <bucket>",
619+
"Enable public access via the r2.dev URL for an R2 bucket",
620+
PublicDevUrl.EnableOptions,
621+
PublicDevUrl.EnableHandler
622+
)
623+
.command(
624+
"disable <bucket>",
625+
"Disable public access via the r2.dev URL for an R2 bucket",
626+
PublicDevUrl.DisableOptions,
627+
PublicDevUrl.DisableHandler
628+
)
629+
.command(
630+
"get <bucket>",
631+
"Get the r2.dev URL and status for an R2 bucket",
632+
PublicDevUrl.GetOptions,
633+
PublicDevUrl.GetHandler
634+
);
635+
}
636+
);
610637
return r2BucketYargs;
611638
});
612639
}

0 commit comments

Comments
 (0)