wrangler 0.0.0-e1c615f → 0.0.0-e5d3690

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/user.tsx CHANGED
@@ -206,7 +206,7 @@
206
206
  */
207
207
 
208
208
  import React from "react";
209
- import { render } from "ink";
209
+ import { render, Text } from "ink";
210
210
  import Table from "ink-table";
211
211
  import SelectInput from "ink-select-input";
212
212
  import fetch from "node-fetch";
@@ -254,38 +254,37 @@ interface AccessToken {
254
254
  expiry: string;
255
255
  }
256
256
 
257
- type Scope =
258
- | "account:read"
259
- | "user:read"
260
- | "workers:write"
261
- | "workers_kv:write"
262
- | "workers_routes:write"
263
- | "workers_scripts:write"
264
- | "workers_tail:read"
265
- | "zone:read"
266
- | "offline_access"; // this should be included by default
267
-
268
- const Scopes: Scope[] = [
269
- "account:read",
270
- "user:read",
271
- "workers:write",
272
- "workers_kv:write",
273
- "workers_routes:write",
274
- "workers_scripts:write",
275
- "workers_tail:read",
276
- "zone:read",
277
- ];
278
-
279
- const ScopeDescriptions = [
280
- "See your account info such as account details, analytics, and memberships.",
281
- "See your user info such as name, email address, and account memberships.",
282
- "See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.",
283
- "See and change Cloudflare Workers KV Storage data such as keys and namespaces.",
284
- "See and change Cloudflare Workers data such as filters and routes.",
285
- "See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
286
- "See Cloudflare Workers tail and script data.",
287
- "Grants read level access to account zone.",
288
- ];
257
+ const Scopes = {
258
+ "account:read":
259
+ "See your account info such as account details, analytics, and memberships.",
260
+ "user:read":
261
+ "See your user info such as name, email address, and account memberships.",
262
+ "workers:write":
263
+ "See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.",
264
+ "workers_kv:write":
265
+ "See and change Cloudflare Workers KV Storage data such as keys and namespaces.",
266
+ "workers_routes:write":
267
+ "See and change Cloudflare Workers data such as filters and routes.",
268
+ "workers_scripts:write":
269
+ "See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
270
+ "workers_tail:read": "See Cloudflare Workers tail and script data.",
271
+ "zone:read": "Grants read level access to account zone.",
272
+ } as const;
273
+
274
+ /**
275
+ * The possible keys for a Scope.
276
+ *
277
+ * "offline_access" is automatically included.
278
+ */
279
+ type Scope = keyof typeof Scopes | "offline_access";
280
+
281
+ const ScopeKeys = Object.keys(Scopes) as Scope[];
282
+
283
+ export function validateScopeKeys(
284
+ scopes: string[]
285
+ ): scopes is typeof ScopeKeys {
286
+ return scopes.every((scope) => Scopes[scope]);
287
+ }
289
288
 
290
289
  const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
291
290
  const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
@@ -298,9 +297,9 @@ let initialised = false;
298
297
 
299
298
  // we do this because we have some async stuff
300
299
  // TODO: this should just happen in the top level
301
- // abd we should fiure out how to do top level await
300
+ // and we should figure out how to do top level await
302
301
  export async function initialise(): Promise<void> {
303
- // get refreshtoken/accesstoken from fs if exists
302
+ // get refreshToken/accessToken from fs if exists
304
303
  try {
305
304
  // if CF_API_TOKEN available, use that
306
305
  if (process.env.CF_API_TOKEN) {
@@ -344,11 +343,10 @@ function throwIfNotInitialised() {
344
343
  }
345
344
  }
346
345
 
347
- export function getAPIToken(): string {
346
+ export function getAPIToken(): string | undefined {
348
347
  if (process.env.CF_API_TOKEN) {
349
348
  return process.env.CF_API_TOKEN;
350
349
  }
351
-
352
350
  throwIfNotInitialised();
353
351
  return LocalState.accessToken?.value;
354
352
  }
@@ -548,7 +546,7 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
548
546
  return true;
549
547
  }
550
548
 
551
- export async function getAuthURL(scopes?: string[]): Promise<string> {
549
+ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
552
550
  const { codeChallenge, codeVerifier } = await generatePKCECodes();
553
551
  const stateQueryParam = generateRandomState(RECOMMENDED_STATE_LENGTH);
554
552
 
@@ -558,16 +556,12 @@ export async function getAuthURL(scopes?: string[]): Promise<string> {
558
556
  stateQueryParam,
559
557
  });
560
558
 
561
- // TODO: verify that the scopes passed are legit
562
-
563
559
  return (
564
560
  AUTH_URL +
565
561
  `?response_type=code&` +
566
562
  `client_id=${encodeURIComponent(CLIENT_ID)}&` +
567
563
  `redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
568
- `scope=${encodeURIComponent(
569
- (scopes || Scopes).concat("offline_access").join(" ")
570
- )}&` +
564
+ `scope=${encodeURIComponent(scopes.concat("offline_access").join(" "))}&` +
571
565
  `state=${stateQueryParam}&` +
572
566
  `code_challenge=${encodeURIComponent(codeChallenge)}&` +
573
567
  `code_challenge_method=S256`
@@ -792,7 +786,7 @@ expiration_time = "${tokenData.token?.expiry}"
792
786
  }
793
787
 
794
788
  type LoginProps = {
795
- scopes?: string[];
789
+ scopes?: Scope[];
796
790
  };
797
791
 
798
792
  export async function loginOrRefreshIfRequired(): Promise<boolean> {
@@ -945,9 +939,9 @@ export async function logout(): Promise<void> {
945
939
  export function listScopes(): void {
946
940
  throwIfNotInitialised();
947
941
  console.log("💁 Available scopes:");
948
- const data = Scopes.map((scope, index) => ({
942
+ const data = ScopeKeys.map((scope) => ({
949
943
  Scope: scope,
950
- Description: ScopeDescriptions[index],
944
+ Description: Scopes[scope],
951
945
  }));
952
946
  render(<Table data={data} />);
953
947
  // TODO: maybe a good idea to show usage here
@@ -971,14 +965,14 @@ export async function getAccountId() {
971
965
  });
972
966
  } catch (err) {
973
967
  // probably offline
968
+ return;
974
969
  }
975
- if (!response) return;
976
- let accountId: string;
977
- // @ts-expect-error need to type this response
978
- const responseJSON: {
970
+
971
+ let accountId: string | undefined;
972
+ const responseJSON = (await response.json()) as {
979
973
  success: boolean;
980
974
  result: { id: string; account: { id: string; name: string } }[];
981
- } = await response.json();
975
+ };
982
976
 
983
977
  if (responseJSON.success === true) {
984
978
  if (responseJSON.result.length === 1) {
@@ -1010,13 +1004,16 @@ export function ChooseAccount(props: {
1010
1004
  onSelect: (item) => void;
1011
1005
  }) {
1012
1006
  return (
1013
- <SelectInput
1014
- items={props.accounts.map((item) => ({
1015
- key: item.id,
1016
- label: item.name,
1017
- value: item,
1018
- }))}
1019
- onSelect={props.onSelect}
1020
- />
1007
+ <>
1008
+ <Text bold>Select an account from below:</Text>
1009
+ <SelectInput
1010
+ items={props.accounts.map((item) => ({
1011
+ key: item.id,
1012
+ label: item.name,
1013
+ value: item,
1014
+ }))}
1015
+ onSelect={props.onSelect}
1016
+ />
1017
+ </>
1021
1018
  );
1022
1019
  }
package/src/whoami.tsx ADDED
@@ -0,0 +1,69 @@
1
+ import { Text, render } from "ink";
2
+ import Table from "ink-table";
3
+ import React from "react";
4
+ import { fetchListResult, fetchResult } from "./cfetch";
5
+ import { getAPIToken } from "./user";
6
+
7
+ export async function whoami() {
8
+ console.log("Getting User settings...");
9
+ const user = await getUserInfo();
10
+ render(<WhoAmI user={user}></WhoAmI>);
11
+ }
12
+
13
+ export function WhoAmI({ user }: { user: UserInfo | undefined }) {
14
+ return user ? (
15
+ <>
16
+ <Email tokenType={user.authType} email={user.email}></Email>
17
+ <Accounts accounts={user.accounts}></Accounts>
18
+ </>
19
+ ) : (
20
+ <Text>You are not authenticated. Please run `wrangler login`.</Text>
21
+ );
22
+ }
23
+
24
+ function Email(props: { tokenType: string; email: string }) {
25
+ return (
26
+ <Text>
27
+ 👋 You are logged in with an {props.tokenType} Token, associated with the
28
+ email &apos;{props.email}&apos;!
29
+ </Text>
30
+ );
31
+ }
32
+
33
+ function Accounts(props: { accounts: AccountInfo[] }) {
34
+ const accounts = props.accounts.map((account) => ({
35
+ "Account Name": account.name,
36
+ "Account ID": account.id,
37
+ }));
38
+ return <Table data={accounts}></Table>;
39
+ }
40
+
41
+ export interface UserInfo {
42
+ apiToken: string;
43
+ authType: string;
44
+ email: string;
45
+ accounts: AccountInfo[];
46
+ }
47
+
48
+ export async function getUserInfo(): Promise<UserInfo | undefined> {
49
+ const apiToken = getAPIToken();
50
+ return apiToken
51
+ ? {
52
+ apiToken,
53
+ authType: "OAuth",
54
+ email: await getEmail(),
55
+ accounts: await getAccounts(),
56
+ }
57
+ : undefined;
58
+ }
59
+
60
+ async function getEmail(): Promise<string> {
61
+ const { email } = await fetchResult<{ email: string }>("/user");
62
+ return email;
63
+ }
64
+
65
+ type AccountInfo = { name: string; id: string };
66
+
67
+ async function getAccounts(): Promise<AccountInfo[]> {
68
+ return await fetchListResult<AccountInfo>("/accounts");
69
+ }