Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the ability to enable, disable, and get r2.dev public access URLs for R2 buckets #7154

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thin-hairs-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

Added the ability to enable, disable, and get r2.dev public access URLs for R2 buckets.
192 changes: 163 additions & 29 deletions packages/wrangler/src/__tests__/r2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ describe("r2", () => {
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
wrangler r2 bucket domain Manage custom domains for an R2 bucket
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket

GLOBAL FLAGS
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
Expand Down Expand Up @@ -134,6 +135,7 @@ describe("r2", () => {
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
wrangler r2 bucket notification Manage event notification rules for an R2 bucket
wrangler r2 bucket domain Manage custom domains for an R2 bucket
wrangler r2 bucket dev-url Manage public access via the r2.dev URL for an R2 bucket

GLOBAL FLAGS
-j, --experimental-json-config Experimental: support wrangler.json [boolean]
Expand Down Expand Up @@ -1510,9 +1512,9 @@ describe("r2", () => {
`r2 bucket domain add ${bucketName} --domain ${domainName} --zone-id ${zoneId}`
);
expect(std.out).toMatchInlineSnapshot(`
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
✨ Custom domain 'example.com' connected successfully."
`);
"Connecting custom domain 'example.com' to bucket 'my-bucket'...
✨ Custom domain 'example.com' connected successfully."
`);
});

it("should error if domain and zone-id are not provided", async () => {
Expand All @@ -1523,10 +1525,10 @@ describe("r2", () => {
`[Error: Missing required arguments: domain, zone-id]`
);
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] Missing required arguments: domain, zone-id
"X [ERROR] Missing required arguments: domain, zone-id

"
`);
"
`);
});
});
describe("list", () => {
Expand Down Expand Up @@ -1574,23 +1576,23 @@ describe("r2", () => {
);
await runWrangler(`r2 bucket domain list ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Listing custom domains connected to bucket 'my-bucket'...
domain: example.com
enabled: Yes
ownership_status: verified
ssl_status: active
min_tls_version: 1.2
zone_id: zone-id-123
zone_name: example-zone

domain: test.com
enabled: No
ownership_status: pending
ssl_status: pending
min_tls_version: 1.0
zone_id: zone-id-456
zone_name: test-zone"
`);
"Listing custom domains connected to bucket 'my-bucket'...
domain: example.com
enabled: Yes
ownership_status: verified
ssl_status: active
min_tls_version: 1.2
zone_id: zone-id-123
zone_name: example-zone

domain: test.com
enabled: No
ownership_status: pending
ssl_status: pending
min_tls_version: 1.0
zone_id: zone-id-456
zone_name: test-zone"
`);
});
});
describe("remove", () => {
Expand Down Expand Up @@ -1625,9 +1627,9 @@ describe("r2", () => {
`r2 bucket domain remove ${bucketName} --domain ${domainName}`
);
expect(std.out).toMatchInlineSnapshot(`
"Removing custom domain 'example.com' from bucket 'my-bucket'...
Custom domain 'example.com' removed successfully."
`);
"Removing custom domain 'example.com' from bucket 'my-bucket'...
Custom domain 'example.com' removed successfully."
`);
});
});
describe("update", () => {
Expand Down Expand Up @@ -1660,9 +1662,141 @@ describe("r2", () => {
`r2 bucket domain update ${bucketName} --domain ${domainName} --min-tls 1.3`
);
expect(std.out).toMatchInlineSnapshot(`
"Updating custom domain 'example.com' for bucket 'my-bucket'...
✨ Custom domain 'example.com' updated successfully."
`);
"Updating custom domain 'example.com' for bucket 'my-bucket'...
✨ Custom domain 'example.com' updated successfully."
`);
});
});
});
describe("dev-url", () => {
const { setIsTTY } = useMockIsTTY();
mockAccountId();
mockApiToken();
describe("get", () => {
it("should retrieve the r2.dev URL of a bucket when public access is enabled", async () => {
const bucketName = "my-bucket";
const domainInfo = {
bucketId: "bucket-id-123",
domain: "pub-bucket-id-123.r2.dev",
enabled: true,
};
msw.use(
http.get(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
async ({ params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
return HttpResponse.json(createFetchResult({ ...domainInfo }));
},
{ once: true }
)
);
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Public access is enabled at 'https://pub-bucket-id-123.r2.dev'."
`);
});

it("should show that public access is disabled when it is disabled", async () => {
const bucketName = "my-bucket";
const domainInfo = {
bucketId: "bucket-id-123",
domain: "pub-bucket-id-123.r2.dev",
enabled: false,
};
msw.use(
http.get(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
async ({ params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
return HttpResponse.json(createFetchResult({ ...domainInfo }));
},
{ once: true }
)
);
await runWrangler(`r2 bucket dev-url get ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Public access via the r2.dev URL is disabled."
`);
});
});

describe("enable", () => {
it("should enable public access", async () => {
const bucketName = "my-bucket";
const domainInfo = {
bucketId: "bucket-id-123",
domain: "pub-bucket-id-123.r2.dev",
enabled: true,
};

setIsTTY(true);
mockConfirm({
text:
`Are you sure you enable public access for bucket '${bucketName}'? ` +
`The contents of your bucket will be made publicly available at its r2.dev URL`,
result: true,
});
msw.use(
http.put(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
async ({ request, params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
const requestBody = await request.json();
expect(requestBody).toEqual({ enabled: true });
return HttpResponse.json(createFetchResult({ ...domainInfo }));
},
{ once: true }
)
);
await runWrangler(`r2 bucket dev-url enable ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Enabling public access for bucket 'my-bucket'...
✨ Public access enabled at 'https://pub-bucket-id-123.r2.dev'."
`);
});
});

describe("disable", () => {
it("should disable public access", async () => {
const bucketName = "my-bucket";
const domainInfo = {
bucketId: "bucket-id-123",
domain: "pub-bucket-id-123.r2.dev",
enabled: false,
};

setIsTTY(true);
mockConfirm({
text:
`Are you sure you disable public access for bucket '${bucketName}'? ` +
`The contents of your bucket will no longer be publicly available at its r2.dev URL`,
result: true,
});
msw.use(
http.put(
"*/accounts/:accountId/r2/buckets/:bucketName/domains/managed",
async ({ request, params }) => {
const { accountId, bucketName: bucketParam } = params;
expect(accountId).toEqual("some-account-id");
expect(bucketParam).toEqual(bucketName);
const requestBody = await request.json();
expect(requestBody).toEqual({ enabled: false });
return HttpResponse.json(createFetchResult({ ...domainInfo }));
},
{ once: true }
)
);
await runWrangler(`r2 bucket dev-url disable ${bucketName}`);
expect(std.out).toMatchInlineSnapshot(`
"Disabling public access for bucket 'my-bucket'...
Public access disabled at 'https://pub-bucket-id-123.r2.dev'."
`);
});
});
});
Expand Down
50 changes: 50 additions & 0 deletions packages/wrangler/src/r2/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,56 @@ export async function configureCustomDomainSettings(
);
}

export interface R2DevDomainInfo {
bucketId: string;
domain: string;
enabled: boolean;
}

export async function getR2DevDomain(
accountId: string,
bucketName: string,
jurisdiction?: string
): Promise<R2DevDomainInfo> {
const headers: HeadersInit = {};
if (jurisdiction) {
headers["cf-r2-jurisdiction"] = jurisdiction;
}

const result = await fetchResult<R2DevDomainInfo>(
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
{
method: "GET",
headers,
}
);
return result;
}

export async function updateR2DevDomain(
accountId: string,
bucketName: string,
enabled: boolean,
jurisdiction?: string
): Promise<R2DevDomainInfo> {
const headers: HeadersInit = {
"Content-Type": "application/json",
};
if (jurisdiction) {
headers["cf-r2-jurisdiction"] = jurisdiction;
}

const result = await fetchResult<R2DevDomainInfo>(
`/accounts/${accountId}/r2/buckets/${bucketName}/domains/managed`,
{
method: "PUT",
headers,
body: JSON.stringify({ enabled }),
}
);
return result;
}

/**
* R2 bucket names must only contain alphanumeric and - characters.
*/
Expand Down
26 changes: 26 additions & 0 deletions packages/wrangler/src/r2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
usingLocalBucket,
} from "./helpers";
import * as Notification from "./notification";
import * as PublicDevUrl from "./public-dev-url";
import * as Sippy from "./sippy";
import type { CommonYargsArgv, SubHelp } from "../yargs-types";
import type { R2PutOptions } from "@cloudflare/workers-types/experimental";
Expand Down Expand Up @@ -640,6 +641,31 @@ export function r2(r2Yargs: CommonYargsArgv, subHelp: SubHelp) {
);
}
);
r2BucketYargs.command(
"dev-url",
"Manage public access via the r2.dev URL for an R2 bucket",
(devUrlYargs) => {
return devUrlYargs
.command(
"enable <bucket>",
"Enable public access via the r2.dev URL for an R2 bucket",
PublicDevUrl.EnableOptions,
PublicDevUrl.EnableHandler
)
.command(
"disable <bucket>",
"Disable public access via the r2.dev URL for an R2 bucket",
PublicDevUrl.DisableOptions,
PublicDevUrl.DisableHandler
)
.command(
"get <bucket>",
"Get the r2.dev URL and status for an R2 bucket",
PublicDevUrl.GetOptions,
PublicDevUrl.GetHandler
);
}
);
return r2BucketYargs;
});
}
Loading
Loading