wrangler 0.0.0-ece06ea → 0.0.0-ed08df4
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/Cloudflare_CA.pem +18 -0
- package/README.md +27 -14
- package/bin/wrangler.js +34 -2
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +71 -36
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +138 -0
- package/pages/functions/filepath-routing.test.ts +234 -0
- package/pages/functions/filepath-routing.ts +189 -0
- package/pages/functions/identifiers.ts +78 -0
- package/pages/functions/routes.ts +156 -0
- package/pages/functions/template-plugin.ts +147 -0
- package/pages/functions/template-worker.ts +143 -0
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +3125 -0
- package/src/__tests__/dev.test.tsx +829 -0
- package/src/__tests__/guess-worker-format.test.ts +85 -0
- package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
- package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
- package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
- package/src/__tests__/helpers/mock-account-id.ts +48 -0
- package/src/__tests__/helpers/mock-bin.ts +36 -0
- package/src/__tests__/helpers/mock-cfetch.ts +211 -0
- package/src/__tests__/helpers/mock-console.ts +87 -0
- package/src/__tests__/helpers/mock-dialogs.ts +90 -0
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +27 -0
- package/src/__tests__/helpers/mock-kv.ts +33 -0
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/mock-stdin.ts +103 -0
- package/src/__tests__/helpers/mock-web-socket.ts +29 -0
- package/src/__tests__/helpers/run-in-tmp.ts +39 -0
- package/src/__tests__/helpers/run-wrangler.ts +16 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
- package/src/__tests__/https-options.test.ts +130 -0
- package/src/__tests__/index.test.ts +221 -132
- package/src/__tests__/init.test.ts +1516 -0
- package/src/__tests__/jest.setup.ts +77 -0
- package/src/__tests__/kv.test.ts +1481 -0
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/package-manager.test.ts +206 -0
- package/src/__tests__/pages.test.ts +327 -0
- package/src/__tests__/parse.test.ts +326 -0
- package/src/__tests__/publish.test.ts +4996 -0
- package/src/__tests__/r2.test.ts +210 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +446 -0
- package/src/__tests__/tail.test.ts +690 -0
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +105 -0
- package/src/bundle.ts +169 -0
- package/src/cfetch/index.ts +104 -0
- package/src/cfetch/internal.ts +140 -0
- package/src/cli.ts +10 -7
- package/src/config/README.md +107 -0
- package/src/config/config.ts +234 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +394 -0
- package/src/config/index.ts +63 -0
- package/src/config/validation-helpers.ts +575 -0
- package/src/config/validation.ts +1424 -0
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +175 -0
- package/src/create-worker-upload-form.ts +240 -0
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +421 -0
- package/src/dev/local.tsx +312 -0
- package/src/dev/remote.tsx +213 -0
- package/src/dev/use-esbuild.ts +118 -0
- package/src/dialogs.tsx +22 -20
- package/src/entry.ts +322 -0
- package/src/environment-variables.ts +35 -0
- package/src/errors.ts +11 -0
- package/src/https-options.ts +123 -0
- package/src/index.tsx +1869 -1020
- package/src/inspect.ts +623 -0
- package/src/intl-polyfill.d.ts +139 -0
- package/src/kv.ts +344 -0
- package/src/logger.ts +66 -0
- package/src/miniflare-cli/README.md +30 -0
- package/src/miniflare-cli/enum-keys.ts +17 -0
- package/src/miniflare-cli/index.ts +41 -0
- package/src/miniflare-cli/request-context.ts +40 -0
- package/src/module-collection.ts +258 -0
- package/src/open-in-browser.ts +26 -0
- package/src/package-manager.ts +127 -0
- package/src/pages.tsx +1756 -218
- package/src/parse.ts +185 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +449 -0
- package/src/publish.ts +377 -262
- package/src/r2.ts +50 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +237 -59
- package/src/tail/filters.ts +279 -0
- package/src/tail/index.ts +299 -0
- package/src/tail/printing.ts +67 -0
- package/src/update-check.ts +19 -0
- package/src/user.tsx +395 -221
- package/src/whoami.tsx +70 -0
- package/src/worker.ts +172 -0
- package/templates/gitignore +171 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +15 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +1 -1
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +108921 -132528
- package/src/__tests__/fixtures/init/.gitkeep +0 -0
- package/src/__tests__/mock-cfetch.js +0 -46
- package/src/api/form_data.ts +0 -158
- package/src/api/inspect.ts +0 -441
- package/src/api/preview.ts +0 -123
- package/src/api/worker.ts +0 -161
- package/src/cfetch.ts +0 -72
- package/src/config.ts +0 -124
- package/src/dev.tsx +0 -736
- package/src/kv.tsx +0 -211
- package/src/tail.tsx +0 -71
- package/src/util/fetch.ts +0 -74
- package/wrangler-dist/cli.js.map +0 -7
package/src/user.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Based heavily on code from https://github.com/BitySA/oauth2-auth-code-pkce */
|
|
2
2
|
|
|
3
|
-
/*
|
|
3
|
+
/*
|
|
4
4
|
|
|
5
5
|
Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -205,25 +205,46 @@
|
|
|
205
205
|
limitations under the License.
|
|
206
206
|
*/
|
|
207
207
|
|
|
208
|
-
import
|
|
209
|
-
import { render } from "ink";
|
|
210
|
-
import Table from "ink-table";
|
|
211
|
-
import SelectInput from "ink-select-input";
|
|
212
|
-
import fetch from "node-fetch";
|
|
208
|
+
import assert from "node:assert";
|
|
213
209
|
import { webcrypto as crypto } from "node:crypto";
|
|
214
|
-
import {
|
|
215
|
-
import open from "open";
|
|
216
|
-
import url from "node:url";
|
|
210
|
+
import { writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
217
211
|
import http from "node:http";
|
|
218
|
-
import { readFile, writeFile, rm, mkdir } from "node:fs/promises";
|
|
219
|
-
import path from "node:path";
|
|
220
|
-
import process from "node:process";
|
|
221
212
|
import os from "node:os";
|
|
213
|
+
import path from "node:path";
|
|
214
|
+
import url from "node:url";
|
|
215
|
+
import { TextEncoder } from "node:util";
|
|
222
216
|
import TOML from "@iarna/toml";
|
|
223
|
-
import
|
|
217
|
+
import { HostURL } from "@webcontainer/env";
|
|
218
|
+
import { render, Text } from "ink";
|
|
219
|
+
import SelectInput from "ink-select-input";
|
|
220
|
+
import Table from "ink-table";
|
|
221
|
+
import React from "react";
|
|
222
|
+
import { fetch } from "undici";
|
|
223
|
+
import { getCloudflareApiBaseUrl } from "./cfetch";
|
|
224
|
+
import { purgeConfigCaches } from "./config-cache";
|
|
225
|
+
import { getEnvironmentVariableFactory } from "./environment-variables";
|
|
226
|
+
import { logger } from "./logger";
|
|
227
|
+
import openInBrowser from "./open-in-browser";
|
|
228
|
+
import { parseTOML, readFileSync } from "./parse";
|
|
229
|
+
import type { Item as SelectInputItem } from "ink-select-input/build/SelectInput";
|
|
224
230
|
import type { ParsedUrlQuery } from "node:querystring";
|
|
225
|
-
import {
|
|
226
|
-
|
|
231
|
+
import type { Response } from "undici";
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Try to read the API token from the environment.
|
|
235
|
+
*/
|
|
236
|
+
const getCloudflareAPITokenFromEnv = getEnvironmentVariableFactory({
|
|
237
|
+
variableName: "CLOUDFLARE_API_TOKEN",
|
|
238
|
+
deprecatedName: "CF_API_TOKEN",
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Try to read the account ID from the environment.
|
|
243
|
+
*/
|
|
244
|
+
const getCloudflareAccountIdFromEnv = getEnvironmentVariableFactory({
|
|
245
|
+
variableName: "CLOUDFLARE_ACCOUNT_ID",
|
|
246
|
+
deprecatedName: "CF_ACCOUNT_ID",
|
|
247
|
+
});
|
|
227
248
|
|
|
228
249
|
/**
|
|
229
250
|
* An implementation of rfc6749#section-4.1 and rfc7636.
|
|
@@ -234,17 +255,45 @@ interface PKCECodes {
|
|
|
234
255
|
codeVerifier: string;
|
|
235
256
|
}
|
|
236
257
|
|
|
237
|
-
|
|
238
|
-
|
|
258
|
+
/**
|
|
259
|
+
* The module level state of the authentication flow.
|
|
260
|
+
*/
|
|
261
|
+
interface State extends AuthTokens {
|
|
239
262
|
authorizationCode?: string;
|
|
240
263
|
codeChallenge?: string;
|
|
241
264
|
codeVerifier?: string;
|
|
242
265
|
hasAuthCodeBeenExchangedForAccessToken?: boolean;
|
|
243
|
-
refreshToken?: RefreshToken; // persist
|
|
244
266
|
stateQueryParam?: string;
|
|
245
267
|
scopes?: Scope[];
|
|
246
268
|
}
|
|
247
269
|
|
|
270
|
+
/**
|
|
271
|
+
* The tokens related to authentication.
|
|
272
|
+
*/
|
|
273
|
+
interface AuthTokens {
|
|
274
|
+
accessToken?: AccessToken;
|
|
275
|
+
refreshToken?: RefreshToken;
|
|
276
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
277
|
+
apiToken?: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The path to the config file that holds user authentication data,
|
|
282
|
+
* relative to the user's home directory.
|
|
283
|
+
*/
|
|
284
|
+
export const USER_AUTH_CONFIG_FILE = ".wrangler/config/default.toml";
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The data that may be read from the `USER_CONFIG_FILE`.
|
|
288
|
+
*/
|
|
289
|
+
export interface UserAuthConfig {
|
|
290
|
+
oauth_token?: string;
|
|
291
|
+
refresh_token?: string;
|
|
292
|
+
expiration_time?: string;
|
|
293
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
294
|
+
api_token?: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
248
297
|
interface RefreshToken {
|
|
249
298
|
value: string;
|
|
250
299
|
}
|
|
@@ -254,103 +303,137 @@ interface AccessToken {
|
|
|
254
303
|
expiry: string;
|
|
255
304
|
}
|
|
256
305
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"
|
|
271
|
-
"
|
|
272
|
-
|
|
273
|
-
"
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
306
|
+
const Scopes = {
|
|
307
|
+
"account:read":
|
|
308
|
+
"See your account info such as account details, analytics, and memberships.",
|
|
309
|
+
"user:read":
|
|
310
|
+
"See your user info such as name, email address, and account memberships.",
|
|
311
|
+
"workers:write":
|
|
312
|
+
"See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.",
|
|
313
|
+
"workers_kv:write":
|
|
314
|
+
"See and change Cloudflare Workers KV Storage data such as keys and namespaces.",
|
|
315
|
+
"workers_routes:write":
|
|
316
|
+
"See and change Cloudflare Workers data such as filters and routes.",
|
|
317
|
+
"workers_scripts:write":
|
|
318
|
+
"See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
|
|
319
|
+
"workers_tail:read": "See Cloudflare Workers tail and script data.",
|
|
320
|
+
"pages:write":
|
|
321
|
+
"See and change Cloudflare Pages projects, settings and deployments.",
|
|
322
|
+
"zone:read": "Grants read level access to account zone.",
|
|
323
|
+
} as const;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* The possible keys for a Scope.
|
|
327
|
+
*
|
|
328
|
+
* "offline_access" is automatically included.
|
|
329
|
+
*/
|
|
330
|
+
type Scope = keyof typeof Scopes;
|
|
331
|
+
|
|
332
|
+
const ScopeKeys = Object.keys(Scopes) as Scope[];
|
|
333
|
+
|
|
334
|
+
export function validateScopeKeys(
|
|
335
|
+
scopes: string[]
|
|
336
|
+
): scopes is typeof ScopeKeys {
|
|
337
|
+
return scopes.every((scope) => scope in Scopes);
|
|
338
|
+
}
|
|
289
339
|
|
|
290
340
|
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
291
341
|
const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
|
|
292
342
|
const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
293
|
-
const CALLBACK_URL = "http://localhost:8976/oauth/callback";
|
|
294
343
|
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
295
344
|
|
|
296
|
-
|
|
297
|
-
|
|
345
|
+
/**
|
|
346
|
+
* To allow OAuth callbacks in environments such as WebContainer we need to
|
|
347
|
+
* create a host URL which only resolves `localhost` to a WebContainer
|
|
348
|
+
* hostname if the process is running in a WebContainer. On local this will
|
|
349
|
+
* be a no-op and it leaves the URL unmodified.
|
|
350
|
+
*
|
|
351
|
+
* @see https://www.npmjs.com/package/@webcontainer/env
|
|
352
|
+
*/
|
|
353
|
+
const CALLBACK_URL = HostURL.parse("http://localhost:8976/oauth/callback").href;
|
|
298
354
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
355
|
+
let LocalState: State = getAuthTokens();
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Compute the current auth tokens.
|
|
359
|
+
*/
|
|
360
|
+
function getAuthTokens(config?: UserAuthConfig): AuthTokens {
|
|
361
|
+
// get refreshToken/accessToken from fs if exists
|
|
304
362
|
try {
|
|
305
|
-
// if
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
363
|
+
// if the environment variable is available, use that
|
|
364
|
+
const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
|
|
365
|
+
if (apiTokenFromEnv) {
|
|
366
|
+
return {
|
|
367
|
+
accessToken: {
|
|
368
|
+
value: apiTokenFromEnv,
|
|
369
|
+
expiry: "3021-12-31T23:59:59+00:00",
|
|
370
|
+
},
|
|
310
371
|
};
|
|
311
|
-
initialised = true;
|
|
312
|
-
return;
|
|
313
372
|
}
|
|
314
373
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
);
|
|
320
|
-
const { oauth_token, refresh_token, expiration_time } = toml as {
|
|
321
|
-
oauth_token: string;
|
|
322
|
-
refresh_token: string;
|
|
323
|
-
expiration_time: string;
|
|
324
|
-
};
|
|
374
|
+
// otherwise try loading from the user auth config file.
|
|
375
|
+
const { oauth_token, refresh_token, expiration_time, api_token } =
|
|
376
|
+
config || readAuthConfigFile();
|
|
377
|
+
|
|
325
378
|
if (oauth_token) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
379
|
+
return {
|
|
380
|
+
accessToken: {
|
|
381
|
+
value: oauth_token,
|
|
382
|
+
// If there is no `expiration_time` field then set it to an old date, to cause it to expire immediately.
|
|
383
|
+
expiry: expiration_time ?? "2000-01-01:00:00:00+00:00",
|
|
384
|
+
},
|
|
385
|
+
refreshToken: { value: refresh_token ?? "" },
|
|
386
|
+
};
|
|
387
|
+
} else if (api_token) {
|
|
388
|
+
return { apiToken: api_token };
|
|
389
|
+
} else {
|
|
390
|
+
return {};
|
|
330
391
|
}
|
|
331
|
-
} catch
|
|
332
|
-
|
|
333
|
-
// console.error(err);
|
|
392
|
+
} catch {
|
|
393
|
+
return {};
|
|
334
394
|
}
|
|
335
|
-
initialised = true;
|
|
336
395
|
}
|
|
337
396
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Run the initialisation of the auth state, in the case that something changed.
|
|
399
|
+
*
|
|
400
|
+
* This runs automatically whenever `writeAuthConfigFile` is run, so generally
|
|
401
|
+
* you won't need to call it yourself.
|
|
402
|
+
*/
|
|
403
|
+
export function reinitialiseAuthTokens(): void;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Reinitialise auth state from an in-memory config, skipping
|
|
407
|
+
* over the part where we write a file and then read it back into memory
|
|
408
|
+
*/
|
|
409
|
+
export function reinitialiseAuthTokens(config: UserAuthConfig): void;
|
|
410
|
+
|
|
411
|
+
export function reinitialiseAuthTokens(config?: UserAuthConfig): void {
|
|
412
|
+
LocalState = getAuthTokens(config);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function getAPIToken(): string | undefined {
|
|
416
|
+
if (LocalState.apiToken) {
|
|
417
|
+
logger.warn(
|
|
418
|
+
"It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
|
|
419
|
+
"This is no longer supported in the current version of Wrangler.\n" +
|
|
420
|
+
"If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
|
|
343
421
|
);
|
|
344
422
|
}
|
|
345
|
-
}
|
|
346
423
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
424
|
+
const localAPIToken = getCloudflareAPITokenFromEnv();
|
|
425
|
+
|
|
426
|
+
if (
|
|
427
|
+
!process.stdout.isTTY &&
|
|
428
|
+
!localAPIToken &&
|
|
429
|
+
!LocalState.accessToken?.value
|
|
430
|
+
) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
|
|
433
|
+
);
|
|
350
434
|
}
|
|
351
435
|
|
|
352
|
-
|
|
353
|
-
return LocalState.accessToken?.value;
|
|
436
|
+
return localAPIToken ?? LocalState.accessToken?.value;
|
|
354
437
|
}
|
|
355
438
|
|
|
356
439
|
interface AccessContext {
|
|
@@ -537,8 +620,8 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
|
|
|
537
620
|
|
|
538
621
|
const stateQueryParam = query.state;
|
|
539
622
|
if (stateQueryParam !== state.stateQueryParam) {
|
|
540
|
-
|
|
541
|
-
"
|
|
623
|
+
logger.warn(
|
|
624
|
+
"Received query string parameter doesn't match the one sent! Possible malicious activity somewhere."
|
|
542
625
|
);
|
|
543
626
|
throw new ErrorInvalidReturnedStateParam();
|
|
544
627
|
}
|
|
@@ -548,7 +631,7 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
|
|
|
548
631
|
return true;
|
|
549
632
|
}
|
|
550
633
|
|
|
551
|
-
export async function getAuthURL(scopes
|
|
634
|
+
export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
552
635
|
const { codeChallenge, codeVerifier } = await generatePKCECodes();
|
|
553
636
|
const stateQueryParam = generateRandomState(RECOMMENDED_STATE_LENGTH);
|
|
554
637
|
|
|
@@ -558,56 +641,79 @@ export async function getAuthURL(scopes?: string[]): Promise<string> {
|
|
|
558
641
|
stateQueryParam,
|
|
559
642
|
});
|
|
560
643
|
|
|
561
|
-
// TODO: verify that the scopes passed are legit
|
|
562
|
-
|
|
563
644
|
return (
|
|
564
645
|
AUTH_URL +
|
|
565
646
|
`?response_type=code&` +
|
|
566
647
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
567
648
|
`redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
)}&` +
|
|
649
|
+
// we add offline_access manually for every request
|
|
650
|
+
`scope=${encodeURIComponent([...scopes, "offline_access"].join(" "))}&` +
|
|
571
651
|
`state=${stateQueryParam}&` +
|
|
572
652
|
`code_challenge=${encodeURIComponent(codeChallenge)}&` +
|
|
573
653
|
`code_challenge_method=S256`
|
|
574
654
|
);
|
|
575
655
|
}
|
|
576
656
|
|
|
657
|
+
type TokenResponse =
|
|
658
|
+
| {
|
|
659
|
+
access_token: string;
|
|
660
|
+
expires_in: number;
|
|
661
|
+
refresh_token: string;
|
|
662
|
+
scope: string;
|
|
663
|
+
}
|
|
664
|
+
| {
|
|
665
|
+
error: string;
|
|
666
|
+
};
|
|
667
|
+
|
|
577
668
|
/**
|
|
578
669
|
* Refresh an access token from the remote service.
|
|
579
670
|
*/
|
|
580
671
|
async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (!refreshToken) {
|
|
584
|
-
console.warn("No refresh token is present.");
|
|
672
|
+
if (!LocalState.refreshToken) {
|
|
673
|
+
logger.warn("No refresh token is present.");
|
|
585
674
|
}
|
|
586
675
|
|
|
587
|
-
const url = TOKEN_URL;
|
|
588
676
|
const body =
|
|
589
677
|
`grant_type=refresh_token&` +
|
|
590
|
-
`refresh_token=${refreshToken?.value}&` +
|
|
678
|
+
`refresh_token=${LocalState.refreshToken?.value}&` +
|
|
591
679
|
`client_id=${CLIENT_ID}`;
|
|
592
680
|
|
|
593
|
-
const response = await fetch(
|
|
681
|
+
const response = await fetch(TOKEN_URL, {
|
|
594
682
|
method: "POST",
|
|
595
683
|
body,
|
|
596
684
|
headers: {
|
|
597
685
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
598
686
|
},
|
|
599
687
|
});
|
|
688
|
+
|
|
600
689
|
if (response.status >= 400) {
|
|
601
|
-
|
|
690
|
+
let tokenExchangeResErr = undefined;
|
|
691
|
+
|
|
692
|
+
try {
|
|
693
|
+
tokenExchangeResErr = await response.text();
|
|
694
|
+
tokenExchangeResErr = JSON.parse(tokenExchangeResErr);
|
|
695
|
+
} catch (e) {
|
|
696
|
+
// If it can't parse to JSON ignore the error
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (tokenExchangeResErr !== undefined) {
|
|
700
|
+
// We will throw the parsed error if it parsed correctly, otherwise we throw an unknown error.
|
|
701
|
+
throw typeof tokenExchangeResErr === "string"
|
|
702
|
+
? new Error(tokenExchangeResErr)
|
|
703
|
+
: tokenExchangeResErr;
|
|
704
|
+
} else {
|
|
705
|
+
throw new ErrorUnknown(
|
|
706
|
+
"Failed to parse Error from exchangeRefreshTokenForAccessToken"
|
|
707
|
+
);
|
|
708
|
+
}
|
|
602
709
|
} else {
|
|
603
710
|
try {
|
|
604
|
-
const json = await response.json();
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
};
|
|
711
|
+
const json = (await response.json()) as TokenResponse;
|
|
712
|
+
if ("error" in json) {
|
|
713
|
+
throw json.error;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const { access_token, expires_in, refresh_token, scope } = json;
|
|
611
717
|
let scopes: Scope[] = [];
|
|
612
718
|
|
|
613
719
|
const accessToken: AccessToken = {
|
|
@@ -617,10 +723,9 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
617
723
|
LocalState.accessToken = accessToken;
|
|
618
724
|
|
|
619
725
|
if (refresh_token) {
|
|
620
|
-
|
|
726
|
+
LocalState.refreshToken = {
|
|
621
727
|
value: refresh_token,
|
|
622
728
|
};
|
|
623
|
-
LocalState.refreshToken = refreshToken;
|
|
624
729
|
}
|
|
625
730
|
|
|
626
731
|
if (scope) {
|
|
@@ -636,20 +741,12 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
636
741
|
refreshToken: LocalState.refreshToken,
|
|
637
742
|
};
|
|
638
743
|
return accessContext;
|
|
639
|
-
} catch (
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
"Expired! Auth code or refresh token needs to be renewed."
|
|
645
|
-
);
|
|
646
|
-
// alert("Redirecting to auth server to obtain a new auth grant code.");
|
|
647
|
-
// TODO: return refreshAuthCodeOrRefreshToken();
|
|
648
|
-
break;
|
|
649
|
-
default:
|
|
650
|
-
break;
|
|
744
|
+
} catch (error) {
|
|
745
|
+
if (typeof error === "string") {
|
|
746
|
+
throw toErrorClass(error);
|
|
747
|
+
} else {
|
|
748
|
+
throw error;
|
|
651
749
|
}
|
|
652
|
-
throw toErrorClass(error);
|
|
653
750
|
}
|
|
654
751
|
}
|
|
655
752
|
}
|
|
@@ -661,12 +758,11 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
661
758
|
const { authorizationCode, codeVerifier = "" } = LocalState;
|
|
662
759
|
|
|
663
760
|
if (!codeVerifier) {
|
|
664
|
-
|
|
761
|
+
logger.warn("No code verifier is being sent.");
|
|
665
762
|
} else if (!authorizationCode) {
|
|
666
|
-
|
|
763
|
+
logger.warn("No authorization grant code is being passed.");
|
|
667
764
|
}
|
|
668
765
|
|
|
669
|
-
const url = TOKEN_URL;
|
|
670
766
|
const body =
|
|
671
767
|
`grant_type=authorization_code&` +
|
|
672
768
|
`code=${encodeURIComponent(authorizationCode || "")}&` +
|
|
@@ -674,7 +770,7 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
674
770
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
675
771
|
`code_verifier=${codeVerifier}`;
|
|
676
772
|
|
|
677
|
-
const response = await fetch(
|
|
773
|
+
const response = await fetch(TOKEN_URL, {
|
|
678
774
|
method: "POST",
|
|
679
775
|
body,
|
|
680
776
|
headers: {
|
|
@@ -685,19 +781,17 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
685
781
|
const { error } = (await response.json()) as { error: string };
|
|
686
782
|
// .catch((_) => ({ error: "invalid_json" }));
|
|
687
783
|
if (error === "invalid_grant") {
|
|
688
|
-
|
|
784
|
+
logger.log("Expired! Auth code or refresh token needs to be renewed.");
|
|
689
785
|
// alert("Redirecting to auth server to obtain a new auth grant code.");
|
|
690
786
|
// TODO: return refreshAuthCodeOrRefreshToken();
|
|
691
787
|
}
|
|
692
788
|
throw toErrorClass(error);
|
|
693
789
|
}
|
|
694
|
-
const json = await response.json();
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
scope: string;
|
|
700
|
-
};
|
|
790
|
+
const json = (await response.json()) as TokenResponse;
|
|
791
|
+
if ("error" in json) {
|
|
792
|
+
throw new Error(json.error);
|
|
793
|
+
}
|
|
794
|
+
const { access_token, expires_in, refresh_token, scope } = json;
|
|
701
795
|
let scopes: Scope[] = [];
|
|
702
796
|
LocalState.hasAuthCodeBeenExchangedForAccessToken = true;
|
|
703
797
|
|
|
@@ -709,10 +803,9 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
709
803
|
LocalState.accessToken = accessToken;
|
|
710
804
|
|
|
711
805
|
if (refresh_token) {
|
|
712
|
-
|
|
806
|
+
LocalState.refreshToken = {
|
|
713
807
|
value: refresh_token,
|
|
714
808
|
};
|
|
715
|
-
LocalState.refreshToken = refreshToken;
|
|
716
809
|
}
|
|
717
810
|
|
|
718
811
|
if (scope) {
|
|
@@ -782,52 +875,73 @@ function generateRandomState(lengthOfState: number): string {
|
|
|
782
875
|
.join("");
|
|
783
876
|
}
|
|
784
877
|
|
|
785
|
-
|
|
786
|
-
|
|
878
|
+
/**
|
|
879
|
+
* Writes a a wrangler config file (auth credentials) to disk,
|
|
880
|
+
* and updates the user auth state with the new credentials.
|
|
881
|
+
*/
|
|
882
|
+
export function writeAuthConfigFile(config: UserAuthConfig) {
|
|
883
|
+
mkdirSync(path.join(os.homedir(), ".wrangler/config/"), {
|
|
787
884
|
recursive: true,
|
|
788
885
|
});
|
|
789
|
-
|
|
790
|
-
path.join(os.homedir(),
|
|
791
|
-
|
|
792
|
-
oauth_token = "${tokenData.token?.value || ""}"
|
|
793
|
-
refresh_token = "${tokenData.refreshToken?.value}"
|
|
794
|
-
expiration_time = "${tokenData.token?.expiry}"
|
|
795
|
-
`,
|
|
886
|
+
writeFileSync(
|
|
887
|
+
path.join(os.homedir(), USER_AUTH_CONFIG_FILE),
|
|
888
|
+
TOML.stringify(config as TOML.JsonMap),
|
|
796
889
|
{ encoding: "utf-8" }
|
|
797
890
|
);
|
|
891
|
+
|
|
892
|
+
reinitialiseAuthTokens();
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export function readAuthConfigFile(): UserAuthConfig {
|
|
896
|
+
const toml = parseTOML(
|
|
897
|
+
readFileSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE))
|
|
898
|
+
);
|
|
899
|
+
return toml;
|
|
798
900
|
}
|
|
799
901
|
|
|
800
902
|
type LoginProps = {
|
|
801
|
-
scopes?:
|
|
903
|
+
scopes?: Scope[];
|
|
802
904
|
};
|
|
803
905
|
|
|
804
|
-
export async function loginOrRefreshIfRequired(
|
|
906
|
+
export async function loginOrRefreshIfRequired(
|
|
907
|
+
isInteractive = true
|
|
908
|
+
): Promise<boolean> {
|
|
805
909
|
// TODO: if there already is a token, then try refreshing
|
|
806
910
|
// TODO: ask permission before opening browser
|
|
807
|
-
if (!
|
|
808
|
-
//
|
|
809
|
-
|
|
911
|
+
if (!getAPIToken()) {
|
|
912
|
+
// Not logged in.
|
|
913
|
+
// If we are not interactive, we cannot ask the user to login
|
|
914
|
+
return isInteractive && (await login());
|
|
810
915
|
} else if (isAccessTokenExpired()) {
|
|
811
|
-
|
|
916
|
+
// We're logged in, but the refresh token seems to have expired,
|
|
917
|
+
// so let's try to refresh it
|
|
918
|
+
const didRefresh = await refreshToken();
|
|
919
|
+
if (didRefresh) {
|
|
920
|
+
// The token was refreshed, so we're done here
|
|
921
|
+
return true;
|
|
922
|
+
} else {
|
|
923
|
+
// If the refresh token isn't valid, then we ask the user to login again
|
|
924
|
+
return isInteractive && (await login());
|
|
925
|
+
}
|
|
812
926
|
} else {
|
|
813
927
|
return true;
|
|
814
928
|
}
|
|
815
929
|
}
|
|
816
930
|
|
|
817
931
|
export async function login(props?: LoginProps): Promise<boolean> {
|
|
932
|
+
logger.log("Attempting to login via OAuth...");
|
|
818
933
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
// console.log(`💁 Opened ${urlToOpen}`);
|
|
822
|
-
let server;
|
|
823
|
-
let loginTimeoutHandle;
|
|
934
|
+
let server: http.Server;
|
|
935
|
+
let loginTimeoutHandle: NodeJS.Timeout;
|
|
824
936
|
const timerPromise = new Promise<boolean>((resolve) => {
|
|
825
937
|
loginTimeoutHandle = setTimeout(() => {
|
|
826
|
-
|
|
938
|
+
logger.error(
|
|
939
|
+
"Timed out waiting for authorization code, please try again."
|
|
940
|
+
);
|
|
827
941
|
server.close();
|
|
828
942
|
clearTimeout(loginTimeoutHandle);
|
|
829
943
|
resolve(false);
|
|
830
|
-
}, 60000); // wait for
|
|
944
|
+
}, 60000); // wait for 60 seconds for the user to authorize
|
|
831
945
|
});
|
|
832
946
|
|
|
833
947
|
const loginPromise = new Promise<boolean>((resolve, reject) => {
|
|
@@ -857,9 +971,10 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
857
971
|
res.end(() => {
|
|
858
972
|
finish(false);
|
|
859
973
|
});
|
|
860
|
-
|
|
861
|
-
"Error: Consent denied. You must grant consent to Wrangler in order to login
|
|
862
|
-
|
|
974
|
+
logger.error(
|
|
975
|
+
"Error: Consent denied. You must grant consent to Wrangler in order to login.\n" +
|
|
976
|
+
"If you don't want to do this consider passing an API token via the `CLOUDFLARE_API_TOKEN` environment variable"
|
|
977
|
+
);
|
|
863
978
|
|
|
864
979
|
return;
|
|
865
980
|
} else {
|
|
@@ -872,8 +987,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
872
987
|
finish(false, new ErrorNoAuthCode());
|
|
873
988
|
return;
|
|
874
989
|
} else {
|
|
875
|
-
const
|
|
876
|
-
|
|
990
|
+
const exchange = await exchangeAuthCodeForAccessToken();
|
|
991
|
+
writeAuthConfigFile({
|
|
992
|
+
oauth_token: exchange.token?.value ?? "",
|
|
993
|
+
expiration_time: exchange.token?.expiry,
|
|
994
|
+
refresh_token: exchange.refreshToken?.value,
|
|
995
|
+
});
|
|
877
996
|
res.writeHead(307, {
|
|
878
997
|
Location:
|
|
879
998
|
"https://welcome.developers.workers.dev/wrangler-oauth-consent-granted",
|
|
@@ -881,9 +1000,9 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
881
1000
|
res.end(() => {
|
|
882
1001
|
finish(true);
|
|
883
1002
|
});
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
);
|
|
1003
|
+
logger.log(`Successfully logged in.`);
|
|
1004
|
+
|
|
1005
|
+
purgeConfigCaches();
|
|
887
1006
|
|
|
888
1007
|
return;
|
|
889
1008
|
}
|
|
@@ -894,42 +1013,64 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
894
1013
|
server.listen(8976);
|
|
895
1014
|
});
|
|
896
1015
|
|
|
1016
|
+
await openInBrowser(urlToOpen);
|
|
1017
|
+
|
|
897
1018
|
return Promise.race([timerPromise, loginPromise]);
|
|
898
1019
|
}
|
|
899
1020
|
|
|
900
1021
|
/**
|
|
901
1022
|
* Checks to see if the access token has expired.
|
|
902
1023
|
*/
|
|
903
|
-
|
|
904
|
-
throwIfNotInitialised();
|
|
1024
|
+
function isAccessTokenExpired(): boolean {
|
|
905
1025
|
const { accessToken } = LocalState;
|
|
906
1026
|
return Boolean(accessToken && new Date() >= new Date(accessToken.expiry));
|
|
907
1027
|
}
|
|
908
1028
|
|
|
909
|
-
|
|
910
|
-
throwIfNotInitialised();
|
|
1029
|
+
async function refreshToken(): Promise<boolean> {
|
|
911
1030
|
// refresh
|
|
912
1031
|
try {
|
|
913
|
-
const
|
|
914
|
-
|
|
1032
|
+
const {
|
|
1033
|
+
token: { value: oauth_token, expiry: expiration_time } = {
|
|
1034
|
+
value: "",
|
|
1035
|
+
expiry: "",
|
|
1036
|
+
},
|
|
1037
|
+
refreshToken: { value: refresh_token } = {},
|
|
1038
|
+
} = await exchangeRefreshTokenForAccessToken();
|
|
1039
|
+
writeAuthConfigFile({ oauth_token, expiration_time, refresh_token });
|
|
915
1040
|
return true;
|
|
916
1041
|
} catch (err) {
|
|
917
|
-
console.error(err);
|
|
918
1042
|
return false;
|
|
919
1043
|
}
|
|
920
1044
|
}
|
|
921
1045
|
|
|
922
1046
|
export async function logout(): Promise<void> {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1047
|
+
if (!LocalState.accessToken) {
|
|
1048
|
+
if (!LocalState.refreshToken) {
|
|
1049
|
+
logger.log("Not logged in, exiting...");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const body =
|
|
1054
|
+
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
1055
|
+
`token_type_hint=refresh_token&` +
|
|
1056
|
+
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1057
|
+
|
|
1058
|
+
const response = await fetch(REVOKE_URL, {
|
|
1059
|
+
method: "POST",
|
|
1060
|
+
body,
|
|
1061
|
+
headers: {
|
|
1062
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
await response.text(); // blank text? would be nice if it was something meaningful
|
|
1066
|
+
logger.log(
|
|
1067
|
+
"💁 Wrangler is configured with an OAuth token. The token has been successfully revoked"
|
|
1068
|
+
);
|
|
928
1069
|
}
|
|
929
1070
|
const body =
|
|
930
1071
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
931
1072
|
`token_type_hint=refresh_token&` +
|
|
932
|
-
`token=${encodeURIComponent(refreshToken?.value || "")}`;
|
|
1073
|
+
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
933
1074
|
|
|
934
1075
|
const response = await fetch(REVOKE_URL, {
|
|
935
1076
|
method: "POST",
|
|
@@ -939,38 +1080,34 @@ export async function logout(): Promise<void> {
|
|
|
939
1080
|
},
|
|
940
1081
|
});
|
|
941
1082
|
await response.text(); // blank text? would be nice if it was something meaningful
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
);
|
|
945
|
-
// delete the file
|
|
946
|
-
await rm(path.join(os.homedir(), ".wrangler/config/default.toml"));
|
|
947
|
-
console.log(
|
|
948
|
-
`Removing ${os.homedir()}/.wrangler/config/default.toml.. success!`
|
|
949
|
-
);
|
|
1083
|
+
rmSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE));
|
|
1084
|
+
logger.log(`Successfully logged out.`);
|
|
950
1085
|
}
|
|
951
1086
|
|
|
952
|
-
export function listScopes(): void {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const data = Scopes.map((scope, index) => ({
|
|
1087
|
+
export function listScopes(message = "💁 Available scopes:"): void {
|
|
1088
|
+
logger.log(message);
|
|
1089
|
+
const data = ScopeKeys.map((scope: Scope) => ({
|
|
956
1090
|
Scope: scope,
|
|
957
|
-
Description:
|
|
1091
|
+
Description: Scopes[scope],
|
|
958
1092
|
}));
|
|
959
1093
|
render(<Table data={data} />);
|
|
960
1094
|
// TODO: maybe a good idea to show usage here
|
|
961
1095
|
}
|
|
962
1096
|
|
|
963
|
-
export async function getAccountId(
|
|
1097
|
+
export async function getAccountId(
|
|
1098
|
+
isInteractive = true
|
|
1099
|
+
): Promise<string | undefined> {
|
|
964
1100
|
const apiToken = getAPIToken();
|
|
965
1101
|
if (!apiToken) return;
|
|
966
1102
|
|
|
967
|
-
|
|
968
|
-
|
|
1103
|
+
const accountIdFromEnv = getCloudflareAccountIdFromEnv();
|
|
1104
|
+
if (accountIdFromEnv) {
|
|
1105
|
+
return accountIdFromEnv;
|
|
969
1106
|
}
|
|
970
1107
|
|
|
971
1108
|
let response: Response;
|
|
972
1109
|
try {
|
|
973
|
-
response = await fetch(`${
|
|
1110
|
+
response = await fetch(`${getCloudflareApiBaseUrl()}/memberships`, {
|
|
974
1111
|
method: "GET",
|
|
975
1112
|
headers: {
|
|
976
1113
|
Authorization: "Bearer " + apiToken,
|
|
@@ -978,19 +1115,19 @@ export async function getAccountId() {
|
|
|
978
1115
|
});
|
|
979
1116
|
} catch (err) {
|
|
980
1117
|
// probably offline
|
|
1118
|
+
return;
|
|
981
1119
|
}
|
|
982
|
-
|
|
983
|
-
let accountId: string;
|
|
984
|
-
|
|
985
|
-
const responseJSON: {
|
|
1120
|
+
|
|
1121
|
+
let accountId: string | undefined;
|
|
1122
|
+
const responseJSON = (await response.json()) as {
|
|
986
1123
|
success: boolean;
|
|
987
1124
|
result: { id: string; account: { id: string; name: string } }[];
|
|
988
|
-
}
|
|
1125
|
+
};
|
|
989
1126
|
|
|
990
1127
|
if (responseJSON.success === true) {
|
|
991
1128
|
if (responseJSON.result.length === 1) {
|
|
992
1129
|
accountId = responseJSON.result[0].account.id;
|
|
993
|
-
} else {
|
|
1130
|
+
} else if (isInteractive) {
|
|
994
1131
|
accountId = await new Promise((resolve) => {
|
|
995
1132
|
const accounts = responseJSON.result.map((x) => x.account);
|
|
996
1133
|
const { unmount } = render(
|
|
@@ -1003,7 +1140,21 @@ export async function getAccountId() {
|
|
|
1003
1140
|
/>
|
|
1004
1141
|
);
|
|
1005
1142
|
});
|
|
1143
|
+
} else {
|
|
1144
|
+
throw new Error(
|
|
1145
|
+
"More than one account available but unable to select one in non-interactive mode.\n" +
|
|
1146
|
+
`Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.\n` +
|
|
1147
|
+
`Available accounts are ("<name>" - "<id>"):\n` +
|
|
1148
|
+
responseJSON.result
|
|
1149
|
+
.map((x) => ` "${x.account.name}" - "${x.account.id}")`)
|
|
1150
|
+
.join("\n")
|
|
1151
|
+
);
|
|
1006
1152
|
}
|
|
1153
|
+
} else {
|
|
1154
|
+
if (!isInteractive)
|
|
1155
|
+
throw new Error(
|
|
1156
|
+
`Failed to automatically retrieve account IDs for the logged in user. In a non-interactive environment, it is mandatory to specify an account ID, either by assigning its value to CLOUDFLARE_ACCOUNT_ID, or as \`account_id\` in your \`wrangler.toml\` file.`
|
|
1157
|
+
);
|
|
1007
1158
|
}
|
|
1008
1159
|
return accountId;
|
|
1009
1160
|
}
|
|
@@ -1014,16 +1165,39 @@ type ChooseAccountItem = {
|
|
|
1014
1165
|
};
|
|
1015
1166
|
export function ChooseAccount(props: {
|
|
1016
1167
|
accounts: ChooseAccountItem[];
|
|
1017
|
-
onSelect: (item) => void;
|
|
1168
|
+
onSelect: (item: SelectInputItem<ChooseAccountItem>) => void;
|
|
1018
1169
|
}) {
|
|
1019
1170
|
return (
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1171
|
+
<>
|
|
1172
|
+
<Text bold>Select an account from below:</Text>
|
|
1173
|
+
<SelectInput
|
|
1174
|
+
items={props.accounts.map((item) => ({
|
|
1175
|
+
key: item.id,
|
|
1176
|
+
label: item.name,
|
|
1177
|
+
value: item,
|
|
1178
|
+
}))}
|
|
1179
|
+
onSelect={props.onSelect}
|
|
1180
|
+
/>
|
|
1181
|
+
</>
|
|
1028
1182
|
);
|
|
1029
1183
|
}
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Ensure that a user is logged in, and a valid account_id is available.
|
|
1187
|
+
*/
|
|
1188
|
+
export async function requireAuth(config: {
|
|
1189
|
+
account_id?: string;
|
|
1190
|
+
}): Promise<string> {
|
|
1191
|
+
const isInteractive = process.stdin.isTTY;
|
|
1192
|
+
const loggedIn = await loginOrRefreshIfRequired(isInteractive);
|
|
1193
|
+
if (!loggedIn) {
|
|
1194
|
+
// didn't login, let's just quit
|
|
1195
|
+
throw new Error("Did not login, quitting...");
|
|
1196
|
+
}
|
|
1197
|
+
const accountId = config.account_id || (await getAccountId(isInteractive));
|
|
1198
|
+
if (!accountId) {
|
|
1199
|
+
throw new Error("No account id found, quitting...");
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
return accountId;
|
|
1203
|
+
}
|