Skip to content

Commit ef7c0b3

Browse files
authored
Added the ability to enable, disable, and get r2.dev public access URLs for R2 buckets. (#7154)
1 parent 9098a3b commit ef7c0b3

File tree

5 files changed

+395
-29
lines changed

5 files changed

+395
-29
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: 163 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ describe("r2", () => {
9999
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
100100
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
101101
wrangler r2 bucket domain Manage custom domains for an R2 bucket
102+
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
102103
103104
GLOBAL FLAGS
104105
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -134,6 +135,7 @@ describe("r2", () => {
134135
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
135136
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
136137
wrangler r2 bucket domain Manage custom domains for an R2 bucket
138+
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket
137139
138140
GLOBAL FLAGS
139141
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
@@ -1510,9 +1512,9 @@ describe("r2", () => {
15101512
`r2 bucket domain add ${bucketName} --domain ${domainName} --zone-id ${zoneId}`
15111513
);
15121514
expect(std.out).toMatchInlineSnapshot(`
1513-
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
1514-
✨ Custom domain 'example.com' connected successfully."
1515-
`);
1515+
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
1516+
✨ Custom domain 'example.com' connected successfully."
1517+
`);
15161518
});
15171519

15181520
it("should error if domain and zone-id are not provided", async () => {
@@ -1523,10 +1525,10 @@ describe("r2", () => {
15231525
`[Error: Missing required arguments: domain, zone-id]`
15241526
);
15251527
expect(std.err).toMatchInlineSnapshot(`
1526-
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing required arguments: domain, zone-id[0m
1528+
"[31mX [41;31m[[41;97mERROR[41;31m][0m [1mMissing required arguments: domain, zone-id[0m
15271529
1528-
"
1529-
`);
1530+
"
1531+
`);
15301532
});
15311533
});
15321534
describe("list", () => {
@@ -1574,23 +1576,23 @@ describe("r2", () => {
15741576
);
15751577
await runWrangler(`r2 bucket domain list ${bucketName}`);
15761578
expect(std.out).toMatchInlineSnapshot(`
1577-
"Listing custom domains connected to bucket 'my-bucket'...
1578-
domain: example.com
1579-
enabled: Yes
1580-
ownership_status: verified
1581-
ssl_status: active
1582-
min_tls_version: 1.2
1583-
zone_id: zone-id-123
1584-
zone_name: example-zone
1585-
1586-
domain: test.com
1587-
enabled: No
1588-
ownership_status: pending
1589-
ssl_status: pending
1590-
min_tls_version: 1.0
1591-
zone_id: zone-id-456
1592-
zone_name: test-zone"
1593-
`);
1579+
"Listing custom domains connected to bucket 'my-bucket'...
1580+
domain: example.com
1581+
enabled: Yes
1582+
ownership_status: verified
1583+
ssl_status: active
1584+
min_tls_version: 1.2
1585+
zone_id: zone-id-123
1586+
zone_name: example-zone
1587+
1588+
domain: test.com
1589+
enabled: No
1590+
ownership_status: pending
1591+
ssl_status: pending
1592+
min_tls_version: 1.0
1593+
zone_id: zone-id-456
1594+
zone_name: test-zone"
1595+
`);
15941596
});
15951597
});
15961598
describe("remove", () => {
@@ -1625,9 +1627,9 @@ describe("r2", () => {
16251627
`r2 bucket domain remove ${bucketName} --domain ${domainName}`
16261628
);
16271629
expect(std.out).toMatchInlineSnapshot(`
1628-
"Removing custom domain 'example.com' from bucket 'my-bucket'...
1629-
Custom domain 'example.com' removed successfully."
1630-
`);
1630+
"Removing custom domain 'example.com' from bucket 'my-bucket'...
1631+
Custom domain 'example.com' removed successfully."
1632+
`);
16311633
});
16321634
});
16331635
describe("update", () => {
@@ -1660,9 +1662,141 @@ describe("r2", () => {
16601662
`r2 bucket domain update ${bucketName} --domain ${domainName} --min-tls 1.3`
16611663
);
16621664
expect(std.out).toMatchInlineSnapshot(`
1663-
"Updating custom domain 'example.com' for bucket 'my-bucket'...
1664-
✨ Custom domain 'example.com' updated successfully."
1665-
`);
1665+
"Updating custom domain 'example.com' for bucket 'my-bucket'...
1666+
✨ Custom domain 'example.com' updated successfully."
1667+
`);
1668+
});
1669+
});
1670+
});
1671+
describe("dev-url", () => {
1672+
const { setIsTTY } = useMockIsTTY();
1673+
mockAccountId();
1674+
mockApiToken();
1675+
describe("get", () => {
1676+
it("should retrieve the r2.dev URL of a bucket when public access is enabled", async () => {
1677+
const bucketName = "my-bucket";
1678+
const domainInfo = {
1679+
bucketId: "bucket-id-123",
1680+
domain: "pub-bucket-id-123.r2.dev",
1681+
enabled: true,
1682+
};
1683+
msw.use(
1684+
http.get(
1685+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1686+
async ({ params }) => {
1687+
const { accountId, bucketName: bucketParam } = params;
1688+
expect(accountId).toEqual("some-account-id");
1689+
expect(bucketParam).toEqual(bucketName);
1690+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1691+
},
1692+
{ once: true }
1693+
)
1694+
);
1695+
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
1696+
expect(std.out).toMatchInlineSnapshot(`
1697+
"Public access is enabled at 'https://pub-bucket-id-123.r2.dev'."
1698+
`);
1699+
});
1700+
1701+
it("should show that public access is disabled when it is disabled", async () => {
1702+
const bucketName = "my-bucket";
1703+
const domainInfo = {
1704+
bucketId: "bucket-id-123",
1705+
domain: "pub-bucket-id-123.r2.dev",
1706+
enabled: false,
1707+
};
1708+
msw.use(
1709+
http.get(
1710+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1711+
async ({ params }) => {
1712+
const { accountId, bucketName: bucketParam } = params;
1713+
expect(accountId).toEqual("some-account-id");
1714+
expect(bucketParam).toEqual(bucketName);
1715+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1716+
},
1717+
{ once: true }
1718+
)
1719+
);
1720+
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
1721+
expect(std.out).toMatchInlineSnapshot(`
1722+
"Public access via the r2.dev URL is disabled."
1723+
`);
1724+
});
1725+
});
1726+
1727+
describe("enable", () => {
1728+
it("should enable public access", async () => {
1729+
const bucketName = "my-bucket";
1730+
const domainInfo = {
1731+
bucketId: "bucket-id-123",
1732+
domain: "pub-bucket-id-123.r2.dev",
1733+
enabled: true,
1734+
};
1735+
1736+
setIsTTY(true);
1737+
mockConfirm({
1738+
text:
1739+
`Are you sure you enable public access for bucket '${bucketName}'? ` +
1740+
`The contents of your bucket will be made publicly available at its r2.dev URL`,
1741+
result: true,
1742+
});
1743+
msw.use(
1744+
http.put(
1745+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1746+
async ({ request, params }) => {
1747+
const { accountId, bucketName: bucketParam } = params;
1748+
expect(accountId).toEqual("some-account-id");
1749+
expect(bucketParam).toEqual(bucketName);
1750+
const requestBody = await request.json();
1751+
expect(requestBody).toEqual({ enabled: true });
1752+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1753+
},
1754+
{ once: true }
1755+
)
1756+
);
1757+
await runWrangler(`r2 bucket dev-url enable ${bucketName}`);
1758+
expect(std.out).toMatchInlineSnapshot(`
1759+
"Enabling public access for bucket 'my-bucket'...
1760+
✨ Public access enabled at 'https://pub-bucket-id-123.r2.dev'."
1761+
`);
1762+
});
1763+
});
1764+
1765+
describe("disable", () => {
1766+
it("should disable public access", async () => {
1767+
const bucketName = "my-bucket";
1768+
const domainInfo = {
1769+
bucketId: "bucket-id-123",
1770+
domain: "pub-bucket-id-123.r2.dev",
1771+
enabled: false,
1772+
};
1773+
1774+
setIsTTY(true);
1775+
mockConfirm({
1776+
text:
1777+
`Are you sure you disable public access for bucket '${bucketName}'? ` +
1778+
`The contents of your bucket will no longer be publicly available at its r2.dev URL`,
1779+
result: true,
1780+
});
1781+
msw.use(
1782+
http.put(
1783+
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
1784+
async ({ request, params }) => {
1785+
const { accountId, bucketName: bucketParam } = params;
1786+
expect(accountId).toEqual("some-account-id");
1787+
expect(bucketParam).toEqual(bucketName);
1788+
const requestBody = await request.json();
1789+
expect(requestBody).toEqual({ enabled: false });
1790+
return HttpResponse.json(createFetchResult({ ...domainInfo }));
1791+
},
1792+
{ once: true }
1793+
)
1794+
);
1795+
await runWrangler(`r2 bucket dev-url disable ${bucketName}`);
1796+
expect(std.out).toMatchInlineSnapshot(`
1797+
"Disabling public access for bucket 'my-bucket'...
1798+
Public access disabled at 'https://pub-bucket-id-123.r2.dev'."
1799+
`);
16661800
});
16671801
});
16681802
});

packages/wrangler/src/r2/helpers.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,56 @@ export async function configureCustomDomainSettings(
742742
);
743743
}
744744

745+
export interface R2DevDomainInfo {
746+
bucketId: string;
747+
domain: string;
748+
enabled: boolean;
749+
}
750+
751+
export async function getR2DevDomain(
752+
accountId: string,
753+
bucketName: string,
754+
jurisdiction?: string
755+
): Promise<R2DevDomainInfo> {
756+
const headers: HeadersInit = {};
757+
if (jurisdiction) {
758+
headers["cf-r2-jurisdiction"] = jurisdiction;
759+
}
760+
761+
const result = await fetchResult<R2DevDomainInfo>(
762+
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
763+
{
764+
method: "GET",
765+
headers,
766+
}
767+
);
768+
return result;
769+
}
770+
771+
export async function updateR2DevDomain(
772+
accountId: string,
773+
bucketName: string,
774+
enabled: boolean,
775+
jurisdiction?: string
776+
): Promise<R2DevDomainInfo> {
777+
const headers: HeadersInit = {
778+
"Content-Type": "application/json",
779+
};
780+
if (jurisdiction) {
781+
headers["cf-r2-jurisdiction"] = jurisdiction;
782+
}
783+
784+
const result = await fetchResult<R2DevDomainInfo>(
785+
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
786+
{
787+
method: "PUT",
788+
headers,
789+
body: JSON.stringify({ enabled }),
790+
}
791+
);
792+
return result;
793+
}
794+
745795
/**
746796
* R2 bucket names must only contain alphanumeric and - characters.
747797
*/

packages/wrangler/src/r2/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
usingLocalBucket,
2525
} from "./helpers";
2626
import * as Notification from "./notification";
27+
import * as PublicDevUrl from "./public-dev-url";
2728
import * as Sippy from "./sippy";
2829
import type { CommonYargsArgv, SubHelp } from "../yargs-types";
2930
import type { R2PutOptions } from "@cloudflare/workers-types/experimental";
@@ -640,6 +641,31 @@ export function r2(r2Yargs: CommonYargsArgv, subHelp: SubHelp) {
640641
);
641642
}
642643
);
644+
r2BucketYargs.command(
645+
"dev-url",
646+
"Manage public access via the r2.dev URL for an R2 bucket",
647+
(devUrlYargs) => {
648+
return devUrlYargs
649+
.command(
650+
"enable <bucket>",
651+
"Enable public access via the r2.dev URL for an R2 bucket",
652+
PublicDevUrl.EnableOptions,
653+
PublicDevUrl.EnableHandler
654+
)
655+
.command(
656+
"disable <bucket>",
657+
"Disable public access via the r2.dev URL for an R2 bucket",
658+
PublicDevUrl.DisableOptions,
659+
PublicDevUrl.DisableHandler
660+
)
661+
.command(
662+
"get <bucket>",
663+
"Get the r2.dev URL and status for an R2 bucket",
664+
PublicDevUrl.GetOptions,
665+
PublicDevUrl.GetHandler
666+
);
667+
}
668+
);
643669
return r2BucketYargs;
644670
});
645671
}

0 commit comments

Comments
 (0)