wrangler 0.0.0-bf67bf2 → 0.0.0-bfbb0be
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 +106 -5
- package/kv-asset-handler.js +1 -0
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +76 -36
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +3548 -0
- package/src/__tests__/dev.test.tsx +1055 -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 +97 -0
- package/src/__tests__/helpers/mock-dialogs.ts +153 -0
- package/src/__tests__/helpers/mock-http-server.ts +46 -0
- package/src/__tests__/helpers/mock-istty.ts +32 -0
- package/src/__tests__/helpers/mock-kv.ts +33 -0
- package/src/__tests__/helpers/mock-oauth-flow.ts +250 -0
- package/src/__tests__/helpers/mock-process.ts +39 -0
- package/src/__tests__/helpers/mock-stdin.ts +108 -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 +235 -130
- package/src/__tests__/init.test.ts +1992 -0
- package/src/__tests__/jest.setup.ts +107 -0
- package/src/__tests__/kv.test.ts +1619 -0
- package/src/__tests__/logger.test.ts +141 -0
- package/src/__tests__/package-manager.test.ts +353 -0
- package/src/__tests__/pages.test.ts +1242 -0
- package/src/__tests__/parse.test.ts +330 -0
- package/src/__tests__/publish.test.ts +6188 -0
- package/src/__tests__/r2.test.ts +233 -0
- package/src/__tests__/route.test.ts +45 -0
- package/src/__tests__/secret.test.ts +486 -0
- package/src/__tests__/tail.test.ts +690 -0
- package/src/__tests__/user.test.ts +132 -0
- package/src/__tests__/whoami.test.tsx +139 -0
- package/src/abort.d.ts +3 -0
- package/src/bundle-reporter.tsx +29 -0
- package/src/bundle.ts +204 -0
- package/src/cfetch/index.ts +121 -0
- package/src/cfetch/internal.ts +142 -0
- package/src/cli.ts +10 -7
- package/src/config/README.md +107 -0
- package/src/config/config.ts +243 -0
- package/src/config/diagnostics.ts +80 -0
- package/src/config/environment.ts +420 -0
- package/src/config/index.ts +225 -0
- package/src/config/validation-helpers.ts +584 -0
- package/src/config/validation.ts +1580 -0
- package/src/config-cache.ts +44 -0
- package/src/create-worker-preview.ts +185 -0
- package/src/create-worker-upload-form.ts +254 -0
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +399 -0
- package/src/dev/local.tsx +321 -0
- package/src/dev/remote.tsx +282 -0
- package/src/dev/use-esbuild.ts +112 -0
- package/src/dialogs.tsx +68 -18
- package/src/durable.ts +102 -0
- package/src/entry.ts +328 -0
- package/src/environment-variables.ts +35 -0
- package/src/errors.ts +11 -0
- package/src/git-client.ts +42 -0
- package/src/https-options.ts +123 -0
- package/src/index.tsx +2092 -1029
- package/src/inspect.ts +662 -0
- package/src/intl-polyfill.d.ts +139 -0
- package/src/kv.ts +408 -0
- package/src/logger.ts +73 -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 +17 -0
- package/src/package-manager.ts +174 -0
- package/src/pages/build.tsx +202 -0
- package/src/pages/constants.ts +7 -0
- package/src/pages/deployments.tsx +101 -0
- package/src/pages/dev.tsx +948 -0
- package/src/pages/functions/buildPlugin.ts +105 -0
- package/src/pages/functions/buildWorker.ts +151 -0
- package/src/pages/functions/filepath-routing.test.ts +234 -0
- package/src/pages/functions/filepath-routing.ts +189 -0
- package/src/pages/functions/identifiers.ts +78 -0
- package/src/pages/functions/routes.ts +151 -0
- package/src/pages/index.tsx +84 -0
- package/src/pages/projects.tsx +157 -0
- package/src/pages/publish.tsx +325 -0
- package/src/pages/types.ts +40 -0
- package/src/pages/upload.tsx +363 -0
- package/src/pages/utils.ts +12 -0
- package/src/parse.ts +222 -0
- package/src/paths.ts +26 -0
- package/src/proxy.ts +476 -0
- package/src/publish.ts +661 -272
- package/src/r2.ts +50 -0
- package/src/selfsigned.d.ts +29 -0
- package/src/sites.tsx +316 -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/choose-account.tsx +60 -0
- package/src/user/env-vars.ts +17 -0
- package/src/user/generate-auth-url.ts +33 -0
- package/src/user/generate-random-state.ts +16 -0
- package/src/user/index.ts +3 -0
- package/src/{user.tsx → user/user.tsx} +380 -276
- package/src/whoami.tsx +71 -0
- package/src/worker.ts +181 -0
- package/src/zones.ts +73 -0
- package/templates/gitignore +171 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.js +15 -0
- package/templates/new-worker.ts +30 -0
- package/templates/no-op-worker.js +10 -0
- package/templates/pages-template-plugin.ts +155 -0
- package/templates/pages-template-worker.ts +161 -0
- package/{static-asset-facade.js → templates/static-asset-facade.js} +3 -7
- package/templates/tsconfig.json +105 -0
- package/wrangler-dist/cli.js +100313 -110298
- 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 -157
- 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/pages.tsx +0 -340
- package/src/tail.tsx +0 -71
- package/src/util/fetch.ts +0 -74
- package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
- package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
- package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
- package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
- package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
- package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
- package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
- package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
- package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
- package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
- package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
- package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
- package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
- package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
- package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
- package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
- package/vendor/wrangler-mime/CHANGELOG.md +0 -289
- package/vendor/wrangler-mime/LICENSE +0 -21
- package/vendor/wrangler-mime/Mime.js +0 -97
- package/vendor/wrangler-mime/README.md +0 -187
- package/vendor/wrangler-mime/cli.js +0 -46
- package/vendor/wrangler-mime/index.js +0 -4
- package/vendor/wrangler-mime/lite.js +0 -4
- package/vendor/wrangler-mime/package.json +0 -52
- package/vendor/wrangler-mime/types/other.js +0 -1
- package/vendor/wrangler-mime/types/standard.js +0 -1
- package/wrangler-dist/cli.js.map +0 -7
|
@@ -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,29 @@
|
|
|
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 } from "ink";
|
|
219
|
+
import Table from "ink-table";
|
|
220
|
+
import React from "react";
|
|
221
|
+
import { fetch } from "undici";
|
|
222
|
+
import { purgeConfigCaches } from "../config-cache";
|
|
223
|
+
import { logger } from "../logger";
|
|
224
|
+
import openInBrowser from "../open-in-browser";
|
|
225
|
+
import { parseTOML, readFileSync } from "../parse";
|
|
226
|
+
import { ChooseAccount, getAccountChoices } from "./choose-account";
|
|
227
|
+
import { getCloudflareAPITokenFromEnv } from "./env-vars";
|
|
228
|
+
import { generateAuthUrl } from "./generate-auth-url";
|
|
229
|
+
import { generateRandomState } from "./generate-random-state";
|
|
224
230
|
import type { ParsedUrlQuery } from "node:querystring";
|
|
225
|
-
import { CF_API_BASE_URL } from "./cfetch";
|
|
226
|
-
import type { Response } from "node-fetch";
|
|
227
231
|
|
|
228
232
|
/**
|
|
229
233
|
* An implementation of rfc6749#section-4.1 and rfc7636.
|
|
@@ -234,17 +238,45 @@ interface PKCECodes {
|
|
|
234
238
|
codeVerifier: string;
|
|
235
239
|
}
|
|
236
240
|
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
/**
|
|
242
|
+
* The module level state of the authentication flow.
|
|
243
|
+
*/
|
|
244
|
+
interface State extends AuthTokens {
|
|
239
245
|
authorizationCode?: string;
|
|
240
246
|
codeChallenge?: string;
|
|
241
247
|
codeVerifier?: string;
|
|
242
248
|
hasAuthCodeBeenExchangedForAccessToken?: boolean;
|
|
243
|
-
refreshToken?: RefreshToken; // persist
|
|
244
249
|
stateQueryParam?: string;
|
|
245
250
|
scopes?: Scope[];
|
|
246
251
|
}
|
|
247
252
|
|
|
253
|
+
/**
|
|
254
|
+
* The tokens related to authentication.
|
|
255
|
+
*/
|
|
256
|
+
interface AuthTokens {
|
|
257
|
+
accessToken?: AccessToken;
|
|
258
|
+
refreshToken?: RefreshToken;
|
|
259
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
260
|
+
apiToken?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The path to the config file that holds user authentication data,
|
|
265
|
+
* relative to the user's home directory.
|
|
266
|
+
*/
|
|
267
|
+
export const USER_AUTH_CONFIG_FILE = ".wrangler/config/default.toml";
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* The data that may be read from the `USER_CONFIG_FILE`.
|
|
271
|
+
*/
|
|
272
|
+
export interface UserAuthConfig {
|
|
273
|
+
oauth_token?: string;
|
|
274
|
+
refresh_token?: string;
|
|
275
|
+
expiration_time?: string;
|
|
276
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
277
|
+
api_token?: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
248
280
|
interface RefreshToken {
|
|
249
281
|
value: string;
|
|
250
282
|
}
|
|
@@ -254,103 +286,137 @@ interface AccessToken {
|
|
|
254
286
|
expiry: string;
|
|
255
287
|
}
|
|
256
288
|
|
|
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
|
-
|
|
289
|
+
const Scopes = {
|
|
290
|
+
"account:read":
|
|
291
|
+
"See your account info such as account details, analytics, and memberships.",
|
|
292
|
+
"user:read":
|
|
293
|
+
"See your user info such as name, email address, and account memberships.",
|
|
294
|
+
"workers:write":
|
|
295
|
+
"See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.",
|
|
296
|
+
"workers_kv:write":
|
|
297
|
+
"See and change Cloudflare Workers KV Storage data such as keys and namespaces.",
|
|
298
|
+
"workers_routes:write":
|
|
299
|
+
"See and change Cloudflare Workers data such as filters and routes.",
|
|
300
|
+
"workers_scripts:write":
|
|
301
|
+
"See and change Cloudflare Workers scripts, durable objects, subdomains, triggers, and tail data.",
|
|
302
|
+
"workers_tail:read": "See Cloudflare Workers tail and script data.",
|
|
303
|
+
"pages:write":
|
|
304
|
+
"See and change Cloudflare Pages projects, settings and deployments.",
|
|
305
|
+
"zone:read": "Grants read level access to account zone.",
|
|
306
|
+
} as const;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* The possible keys for a Scope.
|
|
310
|
+
*
|
|
311
|
+
* "offline_access" is automatically included.
|
|
312
|
+
*/
|
|
313
|
+
type Scope = keyof typeof Scopes;
|
|
314
|
+
|
|
315
|
+
const ScopeKeys = Object.keys(Scopes) as Scope[];
|
|
316
|
+
|
|
317
|
+
export function validateScopeKeys(
|
|
318
|
+
scopes: string[]
|
|
319
|
+
): scopes is typeof ScopeKeys {
|
|
320
|
+
return scopes.every((scope) => scope in Scopes);
|
|
321
|
+
}
|
|
289
322
|
|
|
290
323
|
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
291
324
|
const AUTH_URL = "https://dash.cloudflare.com/oauth2/auth";
|
|
292
325
|
const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
293
|
-
const CALLBACK_URL = "http://localhost:8976/oauth/callback";
|
|
294
326
|
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
295
327
|
|
|
296
|
-
|
|
297
|
-
|
|
328
|
+
/**
|
|
329
|
+
* To allow OAuth callbacks in environments such as WebContainer we need to
|
|
330
|
+
* create a host URL which only resolves `localhost` to a WebContainer
|
|
331
|
+
* hostname if the process is running in a WebContainer. On local this will
|
|
332
|
+
* be a no-op and it leaves the URL unmodified.
|
|
333
|
+
*
|
|
334
|
+
* @see https://www.npmjs.com/package/@webcontainer/env
|
|
335
|
+
*/
|
|
336
|
+
const CALLBACK_URL = HostURL.parse("http://localhost:8976/oauth/callback").href;
|
|
337
|
+
|
|
338
|
+
let LocalState: State = getAuthTokens();
|
|
298
339
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
// get
|
|
340
|
+
/**
|
|
341
|
+
* Compute the current auth tokens.
|
|
342
|
+
*/
|
|
343
|
+
function getAuthTokens(config?: UserAuthConfig): AuthTokens {
|
|
344
|
+
// get refreshToken/accessToken from fs if exists
|
|
304
345
|
try {
|
|
305
|
-
// if
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
346
|
+
// if the environment variable is available, use that
|
|
347
|
+
const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
|
|
348
|
+
if (apiTokenFromEnv) {
|
|
349
|
+
return {
|
|
350
|
+
accessToken: {
|
|
351
|
+
value: apiTokenFromEnv,
|
|
352
|
+
expiry: "3021-12-31T23:59:59+00:00",
|
|
353
|
+
},
|
|
310
354
|
};
|
|
311
|
-
initialised = true;
|
|
312
|
-
return;
|
|
313
355
|
}
|
|
314
356
|
|
|
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
|
-
};
|
|
357
|
+
// otherwise try loading from the user auth config file.
|
|
358
|
+
const { oauth_token, refresh_token, expiration_time, api_token } =
|
|
359
|
+
config || readAuthConfigFile();
|
|
360
|
+
|
|
325
361
|
if (oauth_token) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
362
|
+
return {
|
|
363
|
+
accessToken: {
|
|
364
|
+
value: oauth_token,
|
|
365
|
+
// If there is no `expiration_time` field then set it to an old date, to cause it to expire immediately.
|
|
366
|
+
expiry: expiration_time ?? "2000-01-01:00:00:00+00:00",
|
|
367
|
+
},
|
|
368
|
+
refreshToken: { value: refresh_token ?? "" },
|
|
369
|
+
};
|
|
370
|
+
} else if (api_token) {
|
|
371
|
+
return { apiToken: api_token };
|
|
372
|
+
} else {
|
|
373
|
+
return {};
|
|
330
374
|
}
|
|
331
|
-
} catch
|
|
332
|
-
|
|
333
|
-
// console.error(err);
|
|
375
|
+
} catch {
|
|
376
|
+
return {};
|
|
334
377
|
}
|
|
335
|
-
initialised = true;
|
|
336
378
|
}
|
|
337
379
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Run the initialisation of the auth state, in the case that something changed.
|
|
382
|
+
*
|
|
383
|
+
* This runs automatically whenever `writeAuthConfigFile` is run, so generally
|
|
384
|
+
* you won't need to call it yourself.
|
|
385
|
+
*/
|
|
386
|
+
export function reinitialiseAuthTokens(): void;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Reinitialise auth state from an in-memory config, skipping
|
|
390
|
+
* over the part where we write a file and then read it back into memory
|
|
391
|
+
*/
|
|
392
|
+
export function reinitialiseAuthTokens(config: UserAuthConfig): void;
|
|
393
|
+
|
|
394
|
+
export function reinitialiseAuthTokens(config?: UserAuthConfig): void {
|
|
395
|
+
LocalState = getAuthTokens(config);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function getAPIToken(): string | undefined {
|
|
399
|
+
if (LocalState.apiToken) {
|
|
400
|
+
logger.warn(
|
|
401
|
+
"It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
|
|
402
|
+
"This is no longer supported in the current version of Wrangler.\n" +
|
|
403
|
+
"If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
|
|
343
404
|
);
|
|
344
405
|
}
|
|
345
|
-
}
|
|
346
406
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
407
|
+
const localAPIToken = getCloudflareAPITokenFromEnv();
|
|
408
|
+
|
|
409
|
+
if (
|
|
410
|
+
!process.stdout.isTTY &&
|
|
411
|
+
!localAPIToken &&
|
|
412
|
+
!LocalState.accessToken?.value
|
|
413
|
+
) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
"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."
|
|
416
|
+
);
|
|
350
417
|
}
|
|
351
418
|
|
|
352
|
-
|
|
353
|
-
return LocalState.accessToken?.value;
|
|
419
|
+
return localAPIToken ?? LocalState.accessToken?.value;
|
|
354
420
|
}
|
|
355
421
|
|
|
356
422
|
interface AccessContext {
|
|
@@ -508,7 +574,7 @@ const RECOMMENDED_STATE_LENGTH = 32;
|
|
|
508
574
|
/**
|
|
509
575
|
* Character set to generate code verifier defined in rfc7636.
|
|
510
576
|
*/
|
|
511
|
-
const PKCE_CHARSET =
|
|
577
|
+
export const PKCE_CHARSET =
|
|
512
578
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
513
579
|
|
|
514
580
|
/**
|
|
@@ -537,8 +603,8 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
|
|
|
537
603
|
|
|
538
604
|
const stateQueryParam = query.state;
|
|
539
605
|
if (stateQueryParam !== state.stateQueryParam) {
|
|
540
|
-
|
|
541
|
-
"
|
|
606
|
+
logger.warn(
|
|
607
|
+
"Received query string parameter doesn't match the one sent! Possible malicious activity somewhere."
|
|
542
608
|
);
|
|
543
609
|
throw new ErrorInvalidReturnedStateParam();
|
|
544
610
|
}
|
|
@@ -548,7 +614,7 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
|
|
|
548
614
|
return true;
|
|
549
615
|
}
|
|
550
616
|
|
|
551
|
-
export async function getAuthURL(scopes
|
|
617
|
+
export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
552
618
|
const { codeChallenge, codeVerifier } = await generatePKCECodes();
|
|
553
619
|
const stateQueryParam = generateRandomState(RECOMMENDED_STATE_LENGTH);
|
|
554
620
|
|
|
@@ -558,56 +624,76 @@ export async function getAuthURL(scopes?: string[]): Promise<string> {
|
|
|
558
624
|
stateQueryParam,
|
|
559
625
|
});
|
|
560
626
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
(scopes || Scopes).concat("offline_access").join(" ")
|
|
570
|
-
)}&` +
|
|
571
|
-
`state=${stateQueryParam}&` +
|
|
572
|
-
`code_challenge=${encodeURIComponent(codeChallenge)}&` +
|
|
573
|
-
`code_challenge_method=S256`
|
|
574
|
-
);
|
|
627
|
+
return generateAuthUrl({
|
|
628
|
+
authUrl: AUTH_URL,
|
|
629
|
+
clientId: CLIENT_ID,
|
|
630
|
+
callbackUrl: CALLBACK_URL,
|
|
631
|
+
scopes,
|
|
632
|
+
stateQueryParam,
|
|
633
|
+
codeChallenge,
|
|
634
|
+
});
|
|
575
635
|
}
|
|
576
636
|
|
|
637
|
+
type TokenResponse =
|
|
638
|
+
| {
|
|
639
|
+
access_token: string;
|
|
640
|
+
expires_in: number;
|
|
641
|
+
refresh_token: string;
|
|
642
|
+
scope: string;
|
|
643
|
+
}
|
|
644
|
+
| {
|
|
645
|
+
error: string;
|
|
646
|
+
};
|
|
647
|
+
|
|
577
648
|
/**
|
|
578
649
|
* Refresh an access token from the remote service.
|
|
579
650
|
*/
|
|
580
651
|
async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (!refreshToken) {
|
|
584
|
-
console.warn("No refresh token is present.");
|
|
652
|
+
if (!LocalState.refreshToken) {
|
|
653
|
+
logger.warn("No refresh token is present.");
|
|
585
654
|
}
|
|
586
655
|
|
|
587
|
-
const url = TOKEN_URL;
|
|
588
656
|
const body =
|
|
589
657
|
`grant_type=refresh_token&` +
|
|
590
|
-
`refresh_token=${refreshToken?.value}&` +
|
|
658
|
+
`refresh_token=${LocalState.refreshToken?.value}&` +
|
|
591
659
|
`client_id=${CLIENT_ID}`;
|
|
592
660
|
|
|
593
|
-
const response = await fetch(
|
|
661
|
+
const response = await fetch(TOKEN_URL, {
|
|
594
662
|
method: "POST",
|
|
595
663
|
body,
|
|
596
664
|
headers: {
|
|
597
665
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
598
666
|
},
|
|
599
667
|
});
|
|
668
|
+
|
|
600
669
|
if (response.status >= 400) {
|
|
601
|
-
|
|
670
|
+
let tokenExchangeResErr = undefined;
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
tokenExchangeResErr = await response.text();
|
|
674
|
+
tokenExchangeResErr = JSON.parse(tokenExchangeResErr);
|
|
675
|
+
} catch (e) {
|
|
676
|
+
// If it can't parse to JSON ignore the error
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (tokenExchangeResErr !== undefined) {
|
|
680
|
+
// We will throw the parsed error if it parsed correctly, otherwise we throw an unknown error.
|
|
681
|
+
throw typeof tokenExchangeResErr === "string"
|
|
682
|
+
? new Error(tokenExchangeResErr)
|
|
683
|
+
: tokenExchangeResErr;
|
|
684
|
+
} else {
|
|
685
|
+
throw new ErrorUnknown(
|
|
686
|
+
"Failed to parse Error from exchangeRefreshTokenForAccessToken"
|
|
687
|
+
);
|
|
688
|
+
}
|
|
602
689
|
} else {
|
|
603
690
|
try {
|
|
604
|
-
const json = await response.json();
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
};
|
|
691
|
+
const json = (await response.json()) as TokenResponse;
|
|
692
|
+
if ("error" in json) {
|
|
693
|
+
throw json.error;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const { access_token, expires_in, refresh_token, scope } = json;
|
|
611
697
|
let scopes: Scope[] = [];
|
|
612
698
|
|
|
613
699
|
const accessToken: AccessToken = {
|
|
@@ -617,10 +703,9 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
617
703
|
LocalState.accessToken = accessToken;
|
|
618
704
|
|
|
619
705
|
if (refresh_token) {
|
|
620
|
-
|
|
706
|
+
LocalState.refreshToken = {
|
|
621
707
|
value: refresh_token,
|
|
622
708
|
};
|
|
623
|
-
LocalState.refreshToken = refreshToken;
|
|
624
709
|
}
|
|
625
710
|
|
|
626
711
|
if (scope) {
|
|
@@ -636,20 +721,12 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
636
721
|
refreshToken: LocalState.refreshToken,
|
|
637
722
|
};
|
|
638
723
|
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;
|
|
724
|
+
} catch (error) {
|
|
725
|
+
if (typeof error === "string") {
|
|
726
|
+
throw toErrorClass(error);
|
|
727
|
+
} else {
|
|
728
|
+
throw error;
|
|
651
729
|
}
|
|
652
|
-
throw toErrorClass(error);
|
|
653
730
|
}
|
|
654
731
|
}
|
|
655
732
|
}
|
|
@@ -661,12 +738,11 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
661
738
|
const { authorizationCode, codeVerifier = "" } = LocalState;
|
|
662
739
|
|
|
663
740
|
if (!codeVerifier) {
|
|
664
|
-
|
|
741
|
+
logger.warn("No code verifier is being sent.");
|
|
665
742
|
} else if (!authorizationCode) {
|
|
666
|
-
|
|
743
|
+
logger.warn("No authorization grant code is being passed.");
|
|
667
744
|
}
|
|
668
745
|
|
|
669
|
-
const url = TOKEN_URL;
|
|
670
746
|
const body =
|
|
671
747
|
`grant_type=authorization_code&` +
|
|
672
748
|
`code=${encodeURIComponent(authorizationCode || "")}&` +
|
|
@@ -674,7 +750,7 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
674
750
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
675
751
|
`code_verifier=${codeVerifier}`;
|
|
676
752
|
|
|
677
|
-
const response = await fetch(
|
|
753
|
+
const response = await fetch(TOKEN_URL, {
|
|
678
754
|
method: "POST",
|
|
679
755
|
body,
|
|
680
756
|
headers: {
|
|
@@ -685,19 +761,17 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
685
761
|
const { error } = (await response.json()) as { error: string };
|
|
686
762
|
// .catch((_) => ({ error: "invalid_json" }));
|
|
687
763
|
if (error === "invalid_grant") {
|
|
688
|
-
|
|
764
|
+
logger.log("Expired! Auth code or refresh token needs to be renewed.");
|
|
689
765
|
// alert("Redirecting to auth server to obtain a new auth grant code.");
|
|
690
766
|
// TODO: return refreshAuthCodeOrRefreshToken();
|
|
691
767
|
}
|
|
692
768
|
throw toErrorClass(error);
|
|
693
769
|
}
|
|
694
|
-
const json = await response.json();
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
scope: string;
|
|
700
|
-
};
|
|
770
|
+
const json = (await response.json()) as TokenResponse;
|
|
771
|
+
if ("error" in json) {
|
|
772
|
+
throw new Error(json.error);
|
|
773
|
+
}
|
|
774
|
+
const { access_token, expires_in, refresh_token, scope } = json;
|
|
701
775
|
let scopes: Scope[] = [];
|
|
702
776
|
LocalState.hasAuthCodeBeenExchangedForAccessToken = true;
|
|
703
777
|
|
|
@@ -709,10 +783,9 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
709
783
|
LocalState.accessToken = accessToken;
|
|
710
784
|
|
|
711
785
|
if (refresh_token) {
|
|
712
|
-
|
|
786
|
+
LocalState.refreshToken = {
|
|
713
787
|
value: refresh_token,
|
|
714
788
|
};
|
|
715
|
-
LocalState.refreshToken = refreshToken;
|
|
716
789
|
}
|
|
717
790
|
|
|
718
791
|
if (scope) {
|
|
@@ -771,63 +844,70 @@ async function generatePKCECodes(): Promise<PKCECodes> {
|
|
|
771
844
|
}
|
|
772
845
|
|
|
773
846
|
/**
|
|
774
|
-
*
|
|
847
|
+
* Writes a a wrangler config file (auth credentials) to disk,
|
|
848
|
+
* and updates the user auth state with the new credentials.
|
|
775
849
|
*/
|
|
776
|
-
function
|
|
777
|
-
|
|
778
|
-
// @ts-expect-error crypto's types aren't there yet
|
|
779
|
-
crypto.getRandomValues(output);
|
|
780
|
-
return Array.from(output)
|
|
781
|
-
.map((num: number) => PKCE_CHARSET[num % PKCE_CHARSET.length])
|
|
782
|
-
.join("");
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
async function writeToConfigFile(tokenData: AccessContext) {
|
|
786
|
-
await mkdir(path.join(os.homedir(), ".wrangler/config/"), {
|
|
850
|
+
export function writeAuthConfigFile(config: UserAuthConfig) {
|
|
851
|
+
mkdirSync(path.join(os.homedir(), ".wrangler/config/"), {
|
|
787
852
|
recursive: true,
|
|
788
853
|
});
|
|
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
|
-
`,
|
|
854
|
+
writeFileSync(
|
|
855
|
+
path.join(os.homedir(), USER_AUTH_CONFIG_FILE),
|
|
856
|
+
TOML.stringify(config as TOML.JsonMap),
|
|
796
857
|
{ encoding: "utf-8" }
|
|
797
858
|
);
|
|
859
|
+
|
|
860
|
+
reinitialiseAuthTokens();
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
export function readAuthConfigFile(): UserAuthConfig {
|
|
864
|
+
const toml = parseTOML(
|
|
865
|
+
readFileSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE))
|
|
866
|
+
);
|
|
867
|
+
return toml;
|
|
798
868
|
}
|
|
799
869
|
|
|
800
870
|
type LoginProps = {
|
|
801
|
-
scopes?:
|
|
871
|
+
scopes?: Scope[];
|
|
802
872
|
};
|
|
803
873
|
|
|
804
874
|
export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
805
875
|
// TODO: if there already is a token, then try refreshing
|
|
806
876
|
// TODO: ask permission before opening browser
|
|
807
|
-
if (!
|
|
808
|
-
//
|
|
809
|
-
|
|
877
|
+
if (!getAPIToken()) {
|
|
878
|
+
// Not logged in.
|
|
879
|
+
// If we are not interactive, we cannot ask the user to login
|
|
880
|
+
return isInteractive() && (await login());
|
|
810
881
|
} else if (isAccessTokenExpired()) {
|
|
811
|
-
|
|
882
|
+
// We're logged in, but the refresh token seems to have expired,
|
|
883
|
+
// so let's try to refresh it
|
|
884
|
+
const didRefresh = await refreshToken();
|
|
885
|
+
if (didRefresh) {
|
|
886
|
+
// The token was refreshed, so we're done here
|
|
887
|
+
return true;
|
|
888
|
+
} else {
|
|
889
|
+
// If the refresh token isn't valid, then we ask the user to login again
|
|
890
|
+
return isInteractive() && (await login());
|
|
891
|
+
}
|
|
812
892
|
} else {
|
|
813
893
|
return true;
|
|
814
894
|
}
|
|
815
895
|
}
|
|
816
896
|
|
|
817
897
|
export async function login(props?: LoginProps): Promise<boolean> {
|
|
898
|
+
logger.log("Attempting to login via OAuth...");
|
|
818
899
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
// console.log(`💁 Opened ${urlToOpen}`);
|
|
822
|
-
let server;
|
|
823
|
-
let loginTimeoutHandle;
|
|
900
|
+
let server: http.Server;
|
|
901
|
+
let loginTimeoutHandle: NodeJS.Timeout;
|
|
824
902
|
const timerPromise = new Promise<boolean>((resolve) => {
|
|
825
903
|
loginTimeoutHandle = setTimeout(() => {
|
|
826
|
-
|
|
904
|
+
logger.error(
|
|
905
|
+
"Timed out waiting for authorization code, please try again."
|
|
906
|
+
);
|
|
827
907
|
server.close();
|
|
828
908
|
clearTimeout(loginTimeoutHandle);
|
|
829
909
|
resolve(false);
|
|
830
|
-
}, 60000); // wait for
|
|
910
|
+
}, 60000); // wait for 60 seconds for the user to authorize
|
|
831
911
|
});
|
|
832
912
|
|
|
833
913
|
const loginPromise = new Promise<boolean>((resolve, reject) => {
|
|
@@ -857,9 +937,10 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
857
937
|
res.end(() => {
|
|
858
938
|
finish(false);
|
|
859
939
|
});
|
|
860
|
-
|
|
861
|
-
"Error: Consent denied. You must grant consent to Wrangler in order to login
|
|
862
|
-
|
|
940
|
+
logger.error(
|
|
941
|
+
"Error: Consent denied. You must grant consent to Wrangler in order to login.\n" +
|
|
942
|
+
"If you don't want to do this consider passing an API token via the `CLOUDFLARE_API_TOKEN` environment variable"
|
|
943
|
+
);
|
|
863
944
|
|
|
864
945
|
return;
|
|
865
946
|
} else {
|
|
@@ -872,8 +953,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
872
953
|
finish(false, new ErrorNoAuthCode());
|
|
873
954
|
return;
|
|
874
955
|
} else {
|
|
875
|
-
const
|
|
876
|
-
|
|
956
|
+
const exchange = await exchangeAuthCodeForAccessToken();
|
|
957
|
+
writeAuthConfigFile({
|
|
958
|
+
oauth_token: exchange.token?.value ?? "",
|
|
959
|
+
expiration_time: exchange.token?.expiry,
|
|
960
|
+
refresh_token: exchange.refreshToken?.value,
|
|
961
|
+
});
|
|
877
962
|
res.writeHead(307, {
|
|
878
963
|
Location:
|
|
879
964
|
"https://welcome.developers.workers.dev/wrangler-oauth-consent-granted",
|
|
@@ -881,9 +966,9 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
881
966
|
res.end(() => {
|
|
882
967
|
finish(true);
|
|
883
968
|
});
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
);
|
|
969
|
+
logger.log(`Successfully logged in.`);
|
|
970
|
+
|
|
971
|
+
purgeConfigCaches();
|
|
887
972
|
|
|
888
973
|
return;
|
|
889
974
|
}
|
|
@@ -894,42 +979,65 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
894
979
|
server.listen(8976);
|
|
895
980
|
});
|
|
896
981
|
|
|
982
|
+
logger.log(`Opening a link in your default browser: ${urlToOpen}`);
|
|
983
|
+
await openInBrowser(urlToOpen);
|
|
984
|
+
|
|
897
985
|
return Promise.race([timerPromise, loginPromise]);
|
|
898
986
|
}
|
|
899
987
|
|
|
900
988
|
/**
|
|
901
989
|
* Checks to see if the access token has expired.
|
|
902
990
|
*/
|
|
903
|
-
|
|
904
|
-
throwIfNotInitialised();
|
|
991
|
+
function isAccessTokenExpired(): boolean {
|
|
905
992
|
const { accessToken } = LocalState;
|
|
906
993
|
return Boolean(accessToken && new Date() >= new Date(accessToken.expiry));
|
|
907
994
|
}
|
|
908
995
|
|
|
909
|
-
|
|
910
|
-
throwIfNotInitialised();
|
|
996
|
+
async function refreshToken(): Promise<boolean> {
|
|
911
997
|
// refresh
|
|
912
998
|
try {
|
|
913
|
-
const
|
|
914
|
-
|
|
999
|
+
const {
|
|
1000
|
+
token: { value: oauth_token, expiry: expiration_time } = {
|
|
1001
|
+
value: "",
|
|
1002
|
+
expiry: "",
|
|
1003
|
+
},
|
|
1004
|
+
refreshToken: { value: refresh_token } = {},
|
|
1005
|
+
} = await exchangeRefreshTokenForAccessToken();
|
|
1006
|
+
writeAuthConfigFile({ oauth_token, expiration_time, refresh_token });
|
|
915
1007
|
return true;
|
|
916
1008
|
} catch (err) {
|
|
917
|
-
console.error(err);
|
|
918
1009
|
return false;
|
|
919
1010
|
}
|
|
920
1011
|
}
|
|
921
1012
|
|
|
922
1013
|
export async function logout(): Promise<void> {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1014
|
+
if (!LocalState.accessToken) {
|
|
1015
|
+
if (!LocalState.refreshToken) {
|
|
1016
|
+
logger.log("Not logged in, exiting...");
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const body =
|
|
1021
|
+
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
1022
|
+
`token_type_hint=refresh_token&` +
|
|
1023
|
+
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1024
|
+
|
|
1025
|
+
const response = await fetch(REVOKE_URL, {
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
body,
|
|
1028
|
+
headers: {
|
|
1029
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1030
|
+
},
|
|
1031
|
+
});
|
|
1032
|
+
await response.text(); // blank text? would be nice if it was something meaningful
|
|
1033
|
+
logger.log(
|
|
1034
|
+
"💁 Wrangler is configured with an OAuth token. The token has been successfully revoked"
|
|
1035
|
+
);
|
|
928
1036
|
}
|
|
929
1037
|
const body =
|
|
930
1038
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
931
1039
|
`token_type_hint=refresh_token&` +
|
|
932
|
-
`token=${encodeURIComponent(refreshToken?.value || "")}`;
|
|
1040
|
+
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
933
1041
|
|
|
934
1042
|
const response = await fetch(REVOKE_URL, {
|
|
935
1043
|
method: "POST",
|
|
@@ -939,91 +1047,87 @@ export async function logout(): Promise<void> {
|
|
|
939
1047
|
},
|
|
940
1048
|
});
|
|
941
1049
|
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
|
-
);
|
|
1050
|
+
rmSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE));
|
|
1051
|
+
logger.log(`Successfully logged out.`);
|
|
950
1052
|
}
|
|
951
1053
|
|
|
952
|
-
export function listScopes(): void {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const data = Scopes.map((scope, index) => ({
|
|
1054
|
+
export function listScopes(message = "💁 Available scopes:"): void {
|
|
1055
|
+
logger.log(message);
|
|
1056
|
+
const data = ScopeKeys.map((scope: Scope) => ({
|
|
956
1057
|
Scope: scope,
|
|
957
|
-
Description:
|
|
1058
|
+
Description: Scopes[scope],
|
|
958
1059
|
}));
|
|
959
1060
|
render(<Table data={data} />);
|
|
960
1061
|
// TODO: maybe a good idea to show usage here
|
|
961
1062
|
}
|
|
962
1063
|
|
|
963
|
-
export async function getAccountId() {
|
|
1064
|
+
export async function getAccountId(): Promise<string | undefined> {
|
|
964
1065
|
const apiToken = getAPIToken();
|
|
965
1066
|
if (!apiToken) return;
|
|
966
1067
|
|
|
967
|
-
|
|
968
|
-
|
|
1068
|
+
const accounts = await getAccountChoices();
|
|
1069
|
+
if (accounts.length === 1) {
|
|
1070
|
+
return accounts[0].id;
|
|
969
1071
|
}
|
|
970
1072
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1073
|
+
if (isInteractive()) {
|
|
1074
|
+
return await new Promise((resolve, reject) => {
|
|
1075
|
+
const { unmount } = render(
|
|
1076
|
+
<ChooseAccount
|
|
1077
|
+
accounts={accounts}
|
|
1078
|
+
onSelect={async (selected) => {
|
|
1079
|
+
resolve(selected);
|
|
1080
|
+
unmount();
|
|
1081
|
+
}}
|
|
1082
|
+
onError={(err) => {
|
|
1083
|
+
reject(err);
|
|
1084
|
+
unmount();
|
|
1085
|
+
}}
|
|
1086
|
+
/>
|
|
1087
|
+
);
|
|
978
1088
|
});
|
|
979
|
-
} catch (err) {
|
|
980
|
-
// probably offline
|
|
981
1089
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1090
|
+
|
|
1091
|
+
throw new Error(
|
|
1092
|
+
"More than one account available but unable to select one in non-interactive mode.\n" +
|
|
1093
|
+
`Please set the appropriate \`account_id\` in your \`wrangler.toml\` file.\n` +
|
|
1094
|
+
`Available accounts are ("<name>" - "<id>"):\n` +
|
|
1095
|
+
accounts
|
|
1096
|
+
.map((account) => ` "${account.name}" - "${account.id}")`)
|
|
1097
|
+
.join("\n")
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Ensure that a user is logged in, and a valid account_id is available.
|
|
1103
|
+
*/
|
|
1104
|
+
export async function requireAuth(config: {
|
|
1105
|
+
account_id?: string;
|
|
1106
|
+
}): Promise<string> {
|
|
1107
|
+
const loggedIn = await loginOrRefreshIfRequired();
|
|
1108
|
+
if (!loggedIn) {
|
|
1109
|
+
// didn't login, let's just quit
|
|
1110
|
+
throw new Error("Did not login, quitting...");
|
|
1111
|
+
}
|
|
1112
|
+
const accountId = config.account_id || (await getAccountId());
|
|
1113
|
+
if (!accountId) {
|
|
1114
|
+
throw new Error("No account id found, quitting...");
|
|
1007
1115
|
}
|
|
1116
|
+
|
|
1008
1117
|
return accountId;
|
|
1009
1118
|
}
|
|
1010
1119
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
}
|
|
1019
|
-
return
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
value: item,
|
|
1025
|
-
}))}
|
|
1026
|
-
onSelect={props.onSelect}
|
|
1027
|
-
/>
|
|
1028
|
-
);
|
|
1120
|
+
/**
|
|
1121
|
+
* Throw an error if there is no API token available.
|
|
1122
|
+
*/
|
|
1123
|
+
export function requireApiToken(): string {
|
|
1124
|
+
const authToken = getAPIToken();
|
|
1125
|
+
if (!authToken) {
|
|
1126
|
+
throw new Error("No API token found.");
|
|
1127
|
+
}
|
|
1128
|
+
return authToken;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function isInteractive(): boolean {
|
|
1132
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1029
1133
|
}
|