wrangler 0.0.0-e95bea2 → 0.0.0-e95e5a0
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/README.md +25 -14
- package/bin/wrangler.js +13 -1
- package/miniflare-dist/index.mjs +4886 -0
- package/package.json +33 -25
- package/pages/functions/buildPlugin.ts +92 -0
- package/pages/functions/buildWorker.ts +80 -4
- package/pages/functions/filepath-routing.test.ts +136 -25
- package/pages/functions/filepath-routing.ts +14 -3
- package/pages/functions/routes.ts +6 -1
- package/pages/functions/template-plugin.ts +146 -0
- package/pages/functions/template-worker.ts +21 -8
- package/src/__tests__/config-cache.test.ts +36 -0
- package/src/__tests__/configuration.test.ts +1179 -271
- package/src/__tests__/dev.test.tsx +382 -25
- package/src/__tests__/guess-worker-format.test.ts +28 -1
- package/src/__tests__/helpers/mock-account-id.ts +4 -0
- package/src/__tests__/helpers/mock-cfetch.ts +5 -1
- package/src/__tests__/helpers/mock-console.ts +20 -3
- package/src/__tests__/helpers/mock-dialogs.ts +26 -1
- 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 +9 -16
- package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
- package/src/__tests__/helpers/run-in-tmp.ts +10 -0
- package/src/__tests__/helpers/write-worker-source.ts +31 -0
- package/src/__tests__/https-options.test.ts +5 -2
- package/src/__tests__/index.test.ts +27 -769
- package/src/__tests__/init.test.ts +1516 -0
- package/src/__tests__/jest.setup.ts +4 -4
- package/src/__tests__/kv.test.ts +172 -126
- package/src/__tests__/logger.test.ts +115 -0
- package/src/__tests__/pages.test.ts +311 -26
- package/src/__tests__/publish.test.ts +2946 -524
- package/src/__tests__/r2.test.ts +26 -10
- package/src/__tests__/route.test.ts +4 -4
- package/src/__tests__/secret.test.ts +55 -34
- package/src/__tests__/tail.test.ts +146 -20
- package/src/__tests__/user.test.ts +131 -0
- package/src/__tests__/whoami.test.tsx +26 -7
- package/src/bundle.ts +30 -3
- package/src/cfetch/internal.ts +9 -7
- package/src/cli.ts +5 -11
- package/src/config/README.md +7 -7
- package/src/config/config.ts +33 -78
- package/src/config/environment.ts +93 -4
- package/src/config/index.ts +15 -4
- package/src/config/validation-helpers.ts +202 -15
- package/src/config/validation.ts +530 -133
- package/src/config-cache.ts +45 -0
- package/src/create-worker-preview.ts +22 -13
- package/src/create-worker-upload-form.ts +32 -9
- package/src/dev/dev-vars.ts +36 -0
- package/src/dev/dev.tsx +136 -75
- package/src/dev/local.tsx +196 -125
- package/src/dev/remote.tsx +25 -13
- package/src/dev/use-esbuild.ts +23 -5
- package/src/dialogs.tsx +16 -6
- package/src/entry.ts +119 -11
- package/src/environment-variables.ts +3 -1
- package/src/errors.ts +11 -0
- package/src/https-options.ts +3 -2
- package/src/index.tsx +739 -400
- package/src/inspect.ts +17 -11
- package/src/intl-polyfill.d.ts +139 -0
- package/src/{kv/index.tsx → kv.ts} +8 -28
- 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 +63 -58
- package/src/open-in-browser.ts +5 -4
- package/src/package-manager.ts +9 -8
- package/src/pages.tsx +883 -62
- package/src/parse.ts +1 -1
- package/src/proxy.ts +32 -21
- package/src/publish.ts +276 -161
- package/src/sites.tsx +88 -33
- package/src/tail/index.ts +2 -2
- package/src/tail/printing.ts +13 -8
- package/src/update-check.ts +19 -0
- package/src/user.tsx +231 -103
- package/src/whoami.tsx +2 -1
- package/src/worker.ts +15 -6
- package/templates/gitignore +171 -0
- package/templates/static-asset-facade.js +1 -1
- package/wrangler-dist/cli.js +22087 -49340
- package/src/__tests__/dev2.test.tsx +0 -152
- package/src/__tests__/helpers/mock-user.ts +0 -27
- package/src/__tests__/logout.test.ts +0 -50
- package/src/__tests__/sentry.test.ts +0 -146
- package/src/reporting.ts +0 -131
- package/wrangler-dist/cli.js.map +0 -7
package/src/user.tsx
CHANGED
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
|
|
208
208
|
import assert from "node:assert";
|
|
209
209
|
import { webcrypto as crypto } from "node:crypto";
|
|
210
|
-
import {
|
|
210
|
+
import { writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
211
211
|
import http from "node:http";
|
|
212
212
|
import os from "node:os";
|
|
213
213
|
import path from "node:path";
|
|
@@ -220,8 +220,11 @@ import Table from "ink-table";
|
|
|
220
220
|
import React from "react";
|
|
221
221
|
import { fetch } from "undici";
|
|
222
222
|
import { getCloudflareApiBaseUrl } from "./cfetch";
|
|
223
|
+
import { purgeConfigCaches } from "./config-cache";
|
|
223
224
|
import { getEnvironmentVariableFactory } from "./environment-variables";
|
|
225
|
+
import { logger } from "./logger";
|
|
224
226
|
import openInBrowser from "./open-in-browser";
|
|
227
|
+
import { parseTOML, readFileSync } from "./parse";
|
|
225
228
|
import type { Item as SelectInputItem } from "ink-select-input/build/SelectInput";
|
|
226
229
|
import type { ParsedUrlQuery } from "node:querystring";
|
|
227
230
|
import type { Response } from "undici";
|
|
@@ -251,17 +254,45 @@ interface PKCECodes {
|
|
|
251
254
|
codeVerifier: string;
|
|
252
255
|
}
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
/**
|
|
258
|
+
* The module level state of the authentication flow.
|
|
259
|
+
*/
|
|
260
|
+
interface State extends AuthTokens {
|
|
256
261
|
authorizationCode?: string;
|
|
257
262
|
codeChallenge?: string;
|
|
258
263
|
codeVerifier?: string;
|
|
259
264
|
hasAuthCodeBeenExchangedForAccessToken?: boolean;
|
|
260
|
-
refreshToken?: RefreshToken; // persist
|
|
261
265
|
stateQueryParam?: string;
|
|
262
266
|
scopes?: Scope[];
|
|
263
267
|
}
|
|
264
268
|
|
|
269
|
+
/**
|
|
270
|
+
* The tokens related to authentication.
|
|
271
|
+
*/
|
|
272
|
+
interface AuthTokens {
|
|
273
|
+
accessToken?: AccessToken;
|
|
274
|
+
refreshToken?: RefreshToken;
|
|
275
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
276
|
+
apiToken?: string;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The path to the config file that holds user authentication data,
|
|
281
|
+
* relative to the user's home directory.
|
|
282
|
+
*/
|
|
283
|
+
export const USER_AUTH_CONFIG_FILE = ".wrangler/config/default.toml";
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* The data that may be read from the `USER_CONFIG_FILE`.
|
|
287
|
+
*/
|
|
288
|
+
export interface UserAuthConfig {
|
|
289
|
+
oauth_token?: string;
|
|
290
|
+
refresh_token?: string;
|
|
291
|
+
expiration_time?: string;
|
|
292
|
+
/** @deprecated - this field was only provided by the deprecated `wrangler1 config` command. */
|
|
293
|
+
api_token?: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
265
296
|
interface RefreshToken {
|
|
266
297
|
value: string;
|
|
267
298
|
}
|
|
@@ -311,61 +342,88 @@ const TOKEN_URL = "https://dash.cloudflare.com/oauth2/token";
|
|
|
311
342
|
const CALLBACK_URL = "http://localhost:8976/oauth/callback";
|
|
312
343
|
const REVOKE_URL = "https://dash.cloudflare.com/oauth2/revoke";
|
|
313
344
|
|
|
314
|
-
|
|
315
|
-
let initialised = false;
|
|
345
|
+
let LocalState: State = getAuthTokens();
|
|
316
346
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Compute the current auth tokens.
|
|
349
|
+
*/
|
|
350
|
+
function getAuthTokens(config?: UserAuthConfig): AuthTokens {
|
|
321
351
|
// get refreshToken/accessToken from fs if exists
|
|
322
352
|
try {
|
|
323
353
|
// if the environment variable is available, use that
|
|
324
354
|
const apiTokenFromEnv = getCloudflareAPITokenFromEnv();
|
|
325
355
|
if (apiTokenFromEnv) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
356
|
+
return {
|
|
357
|
+
accessToken: {
|
|
358
|
+
value: apiTokenFromEnv,
|
|
359
|
+
expiry: "3021-12-31T23:59:59+00:00",
|
|
360
|
+
},
|
|
329
361
|
};
|
|
330
|
-
initialised = true;
|
|
331
|
-
return;
|
|
332
362
|
}
|
|
333
363
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
);
|
|
339
|
-
const { oauth_token, refresh_token, expiration_time } = toml as {
|
|
340
|
-
oauth_token: string;
|
|
341
|
-
refresh_token: string;
|
|
342
|
-
expiration_time: string;
|
|
343
|
-
};
|
|
364
|
+
// otherwise try loading from the user auth config file.
|
|
365
|
+
const { oauth_token, refresh_token, expiration_time, api_token } =
|
|
366
|
+
config || readAuthConfigFile();
|
|
367
|
+
|
|
344
368
|
if (oauth_token) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
369
|
+
return {
|
|
370
|
+
accessToken: {
|
|
371
|
+
value: oauth_token,
|
|
372
|
+
// If there is no `expiration_time` field then set it to an old date, to cause it to expire immediately.
|
|
373
|
+
expiry: expiration_time ?? "2000-01-01:00:00:00+00:00",
|
|
374
|
+
},
|
|
375
|
+
refreshToken: { value: refresh_token ?? "" },
|
|
376
|
+
};
|
|
377
|
+
} else if (api_token) {
|
|
378
|
+
return { apiToken: api_token };
|
|
379
|
+
} else {
|
|
380
|
+
return {};
|
|
349
381
|
}
|
|
350
|
-
} catch
|
|
351
|
-
|
|
352
|
-
// console.error(err);
|
|
382
|
+
} catch {
|
|
383
|
+
return {};
|
|
353
384
|
}
|
|
354
|
-
initialised = true;
|
|
355
385
|
}
|
|
356
386
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Run the initialisation of the auth state, in the case that something changed.
|
|
389
|
+
*
|
|
390
|
+
* This runs automatically whenever `writeAuthConfigFile` is run, so generally
|
|
391
|
+
* you won't need to call it yourself.
|
|
392
|
+
*/
|
|
393
|
+
export function reinitialiseAuthTokens(): void;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Reinitialise auth state from an in-memory config, skipping
|
|
397
|
+
* over the part where we write a file and then read it back into memory
|
|
398
|
+
*/
|
|
399
|
+
export function reinitialiseAuthTokens(config: UserAuthConfig): void;
|
|
400
|
+
|
|
401
|
+
export function reinitialiseAuthTokens(config?: UserAuthConfig): void {
|
|
402
|
+
LocalState = getAuthTokens(config);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function getAPIToken(): string | undefined {
|
|
406
|
+
if (LocalState.apiToken) {
|
|
407
|
+
logger.warn(
|
|
408
|
+
"It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
|
|
409
|
+
"This is no longer supported in the current version of Wrangler.\n" +
|
|
410
|
+
"If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const localAPIToken = getCloudflareAPITokenFromEnv();
|
|
415
|
+
|
|
416
|
+
if (
|
|
417
|
+
!process.stdout.isTTY &&
|
|
418
|
+
!localAPIToken &&
|
|
419
|
+
!LocalState.accessToken?.value
|
|
420
|
+
) {
|
|
360
421
|
throw new Error(
|
|
361
|
-
"
|
|
422
|
+
"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."
|
|
362
423
|
);
|
|
363
424
|
}
|
|
364
|
-
}
|
|
365
425
|
|
|
366
|
-
|
|
367
|
-
throwIfNotInitialised();
|
|
368
|
-
return LocalState.accessToken?.value;
|
|
426
|
+
return localAPIToken ?? LocalState.accessToken?.value;
|
|
369
427
|
}
|
|
370
428
|
|
|
371
429
|
interface AccessContext {
|
|
@@ -552,8 +610,8 @@ function isReturningFromAuthServer(query: ParsedUrlQuery): boolean {
|
|
|
552
610
|
|
|
553
611
|
const stateQueryParam = query.state;
|
|
554
612
|
if (stateQueryParam !== state.stateQueryParam) {
|
|
555
|
-
|
|
556
|
-
"
|
|
613
|
+
logger.warn(
|
|
614
|
+
"Received query string parameter doesn't match the one sent! Possible malicious activity somewhere."
|
|
557
615
|
);
|
|
558
616
|
throw new ErrorInvalidReturnedStateParam();
|
|
559
617
|
}
|
|
@@ -578,8 +636,8 @@ export async function getAuthURL(scopes = ScopeKeys): Promise<string> {
|
|
|
578
636
|
`?response_type=code&` +
|
|
579
637
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
580
638
|
`redirect_uri=${encodeURIComponent(CALLBACK_URL)}&` +
|
|
581
|
-
//
|
|
582
|
-
`scope=${encodeURIComponent(scopes
|
|
639
|
+
// we add offline_access manually for every request
|
|
640
|
+
`scope=${encodeURIComponent([...scopes, "offline_access"].join(" "))}&` +
|
|
583
641
|
`state=${stateQueryParam}&` +
|
|
584
642
|
`code_challenge=${encodeURIComponent(codeChallenge)}&` +
|
|
585
643
|
`code_challenge_method=S256`
|
|
@@ -602,7 +660,7 @@ type TokenResponse =
|
|
|
602
660
|
*/
|
|
603
661
|
async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
604
662
|
if (!LocalState.refreshToken) {
|
|
605
|
-
|
|
663
|
+
logger.warn("No refresh token is present.");
|
|
606
664
|
}
|
|
607
665
|
|
|
608
666
|
const body =
|
|
@@ -617,8 +675,27 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
617
675
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
618
676
|
},
|
|
619
677
|
});
|
|
678
|
+
|
|
620
679
|
if (response.status >= 400) {
|
|
621
|
-
|
|
680
|
+
let tokenExchangeResErr = undefined;
|
|
681
|
+
|
|
682
|
+
try {
|
|
683
|
+
tokenExchangeResErr = await response.text();
|
|
684
|
+
tokenExchangeResErr = JSON.parse(tokenExchangeResErr);
|
|
685
|
+
} catch (e) {
|
|
686
|
+
// If it can't parse to JSON ignore the error
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (tokenExchangeResErr !== undefined) {
|
|
690
|
+
// We will throw the parsed error if it parsed correctly, otherwise we throw an unknown error.
|
|
691
|
+
throw typeof tokenExchangeResErr === "string"
|
|
692
|
+
? new Error(tokenExchangeResErr)
|
|
693
|
+
: tokenExchangeResErr;
|
|
694
|
+
} else {
|
|
695
|
+
throw new ErrorUnknown(
|
|
696
|
+
"Failed to parse Error from exchangeRefreshTokenForAccessToken"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
622
699
|
} else {
|
|
623
700
|
try {
|
|
624
701
|
const json = (await response.json()) as TokenResponse;
|
|
@@ -655,18 +732,6 @@ async function exchangeRefreshTokenForAccessToken(): Promise<AccessContext> {
|
|
|
655
732
|
};
|
|
656
733
|
return accessContext;
|
|
657
734
|
} catch (error) {
|
|
658
|
-
switch (error) {
|
|
659
|
-
case "invalid_grant":
|
|
660
|
-
console.warn(
|
|
661
|
-
"Expired! Auth code or refresh token needs to be renewed."
|
|
662
|
-
);
|
|
663
|
-
// alert("Redirecting to auth server to obtain a new auth grant code.");
|
|
664
|
-
// TODO: return refreshAuthCodeOrRefreshToken();
|
|
665
|
-
break;
|
|
666
|
-
default:
|
|
667
|
-
console.error(error);
|
|
668
|
-
break;
|
|
669
|
-
}
|
|
670
735
|
if (typeof error === "string") {
|
|
671
736
|
throw toErrorClass(error);
|
|
672
737
|
} else {
|
|
@@ -683,9 +748,9 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
683
748
|
const { authorizationCode, codeVerifier = "" } = LocalState;
|
|
684
749
|
|
|
685
750
|
if (!codeVerifier) {
|
|
686
|
-
|
|
751
|
+
logger.warn("No code verifier is being sent.");
|
|
687
752
|
} else if (!authorizationCode) {
|
|
688
|
-
|
|
753
|
+
logger.warn("No authorization grant code is being passed.");
|
|
689
754
|
}
|
|
690
755
|
|
|
691
756
|
const body =
|
|
@@ -706,7 +771,7 @@ async function exchangeAuthCodeForAccessToken(): Promise<AccessContext> {
|
|
|
706
771
|
const { error } = (await response.json()) as { error: string };
|
|
707
772
|
// .catch((_) => ({ error: "invalid_json" }));
|
|
708
773
|
if (error === "invalid_grant") {
|
|
709
|
-
|
|
774
|
+
logger.log("Expired! Auth code or refresh token needs to be renewed.");
|
|
710
775
|
// alert("Redirecting to auth server to obtain a new auth grant code.");
|
|
711
776
|
// TODO: return refreshAuthCodeOrRefreshToken();
|
|
712
777
|
}
|
|
@@ -800,19 +865,28 @@ function generateRandomState(lengthOfState: number): string {
|
|
|
800
865
|
.join("");
|
|
801
866
|
}
|
|
802
867
|
|
|
803
|
-
|
|
804
|
-
|
|
868
|
+
/**
|
|
869
|
+
* Writes a a wrangler config file (auth credentials) to disk,
|
|
870
|
+
* and updates the user auth state with the new credentials.
|
|
871
|
+
*/
|
|
872
|
+
export function writeAuthConfigFile(config: UserAuthConfig) {
|
|
873
|
+
mkdirSync(path.join(os.homedir(), ".wrangler/config/"), {
|
|
805
874
|
recursive: true,
|
|
806
875
|
});
|
|
807
|
-
|
|
808
|
-
path.join(os.homedir(),
|
|
809
|
-
|
|
810
|
-
oauth_token = "${tokenData.token?.value || ""}"
|
|
811
|
-
refresh_token = "${tokenData.refreshToken?.value}"
|
|
812
|
-
expiration_time = "${tokenData.token?.expiry}"
|
|
813
|
-
`,
|
|
876
|
+
writeFileSync(
|
|
877
|
+
path.join(os.homedir(), USER_AUTH_CONFIG_FILE),
|
|
878
|
+
TOML.stringify(config as TOML.JsonMap),
|
|
814
879
|
{ encoding: "utf-8" }
|
|
815
880
|
);
|
|
881
|
+
|
|
882
|
+
reinitialiseAuthTokens();
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
export function readAuthConfigFile(): UserAuthConfig {
|
|
886
|
+
const toml = parseTOML(
|
|
887
|
+
readFileSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE))
|
|
888
|
+
);
|
|
889
|
+
return toml;
|
|
816
890
|
}
|
|
817
891
|
|
|
818
892
|
type LoginProps = {
|
|
@@ -824,25 +898,34 @@ export async function loginOrRefreshIfRequired(
|
|
|
824
898
|
): Promise<boolean> {
|
|
825
899
|
// TODO: if there already is a token, then try refreshing
|
|
826
900
|
// TODO: ask permission before opening browser
|
|
827
|
-
if (!
|
|
901
|
+
if (!getAPIToken()) {
|
|
828
902
|
// Not logged in.
|
|
829
903
|
// If we are not interactive, we cannot ask the user to login
|
|
830
904
|
return isInteractive && (await login());
|
|
831
905
|
} else if (isAccessTokenExpired()) {
|
|
832
|
-
|
|
906
|
+
// We're logged in, but the refresh token seems to have expired,
|
|
907
|
+
// so let's try to refresh it
|
|
908
|
+
const didRefresh = await refreshToken();
|
|
909
|
+
if (didRefresh) {
|
|
910
|
+
// The token was refreshed, so we're done here
|
|
911
|
+
return true;
|
|
912
|
+
} else {
|
|
913
|
+
// If the refresh token isn't valid, then we ask the user to login again
|
|
914
|
+
return isInteractive && (await login());
|
|
915
|
+
}
|
|
833
916
|
} else {
|
|
834
917
|
return true;
|
|
835
918
|
}
|
|
836
919
|
}
|
|
837
920
|
|
|
838
921
|
export async function login(props?: LoginProps): Promise<boolean> {
|
|
922
|
+
logger.log("Attempting to login via OAuth...");
|
|
839
923
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
840
|
-
await openInBrowser(urlToOpen);
|
|
841
924
|
let server: http.Server;
|
|
842
925
|
let loginTimeoutHandle: NodeJS.Timeout;
|
|
843
926
|
const timerPromise = new Promise<boolean>((resolve) => {
|
|
844
927
|
loginTimeoutHandle = setTimeout(() => {
|
|
845
|
-
|
|
928
|
+
logger.error(
|
|
846
929
|
"Timed out waiting for authorization code, please try again."
|
|
847
930
|
);
|
|
848
931
|
server.close();
|
|
@@ -878,7 +961,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
878
961
|
res.end(() => {
|
|
879
962
|
finish(false);
|
|
880
963
|
});
|
|
881
|
-
|
|
964
|
+
logger.error(
|
|
882
965
|
"Error: Consent denied. You must grant consent to Wrangler in order to login.\n" +
|
|
883
966
|
"If you don't want to do this consider passing an API token via the `CLOUDFLARE_API_TOKEN` environment variable"
|
|
884
967
|
);
|
|
@@ -894,8 +977,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
894
977
|
finish(false, new ErrorNoAuthCode());
|
|
895
978
|
return;
|
|
896
979
|
} else {
|
|
897
|
-
const
|
|
898
|
-
|
|
980
|
+
const exchange = await exchangeAuthCodeForAccessToken();
|
|
981
|
+
writeAuthConfigFile({
|
|
982
|
+
oauth_token: exchange.token?.value ?? "",
|
|
983
|
+
expiration_time: exchange.token?.expiry,
|
|
984
|
+
refresh_token: exchange.refreshToken?.value,
|
|
985
|
+
});
|
|
899
986
|
res.writeHead(307, {
|
|
900
987
|
Location:
|
|
901
988
|
"https://welcome.developers.workers.dev/wrangler-oauth-consent-granted",
|
|
@@ -903,9 +990,9 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
903
990
|
res.end(() => {
|
|
904
991
|
finish(true);
|
|
905
992
|
});
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
);
|
|
993
|
+
logger.log(`Successfully logged in.`);
|
|
994
|
+
|
|
995
|
+
purgeConfigCaches();
|
|
909
996
|
|
|
910
997
|
return;
|
|
911
998
|
}
|
|
@@ -916,36 +1003,59 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
916
1003
|
server.listen(8976);
|
|
917
1004
|
});
|
|
918
1005
|
|
|
1006
|
+
await openInBrowser(urlToOpen);
|
|
1007
|
+
|
|
919
1008
|
return Promise.race([timerPromise, loginPromise]);
|
|
920
1009
|
}
|
|
921
1010
|
|
|
922
1011
|
/**
|
|
923
1012
|
* Checks to see if the access token has expired.
|
|
924
1013
|
*/
|
|
925
|
-
|
|
926
|
-
throwIfNotInitialised();
|
|
1014
|
+
function isAccessTokenExpired(): boolean {
|
|
927
1015
|
const { accessToken } = LocalState;
|
|
928
1016
|
return Boolean(accessToken && new Date() >= new Date(accessToken.expiry));
|
|
929
1017
|
}
|
|
930
1018
|
|
|
931
|
-
|
|
932
|
-
throwIfNotInitialised();
|
|
1019
|
+
async function refreshToken(): Promise<boolean> {
|
|
933
1020
|
// refresh
|
|
934
1021
|
try {
|
|
935
|
-
const
|
|
936
|
-
|
|
1022
|
+
const {
|
|
1023
|
+
token: { value: oauth_token, expiry: expiration_time } = {
|
|
1024
|
+
value: "",
|
|
1025
|
+
expiry: "",
|
|
1026
|
+
},
|
|
1027
|
+
refreshToken: { value: refresh_token } = {},
|
|
1028
|
+
} = await exchangeRefreshTokenForAccessToken();
|
|
1029
|
+
writeAuthConfigFile({ oauth_token, expiration_time, refresh_token });
|
|
937
1030
|
return true;
|
|
938
1031
|
} catch (err) {
|
|
939
|
-
console.error(err);
|
|
940
1032
|
return false;
|
|
941
1033
|
}
|
|
942
1034
|
}
|
|
943
1035
|
|
|
944
1036
|
export async function logout(): Promise<void> {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1037
|
+
if (!LocalState.accessToken) {
|
|
1038
|
+
if (!LocalState.refreshToken) {
|
|
1039
|
+
logger.log("Not logged in, exiting...");
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const body =
|
|
1044
|
+
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
1045
|
+
`token_type_hint=refresh_token&` +
|
|
1046
|
+
`token=${encodeURIComponent(LocalState.refreshToken?.value || "")}`;
|
|
1047
|
+
|
|
1048
|
+
const response = await fetch(REVOKE_URL, {
|
|
1049
|
+
method: "POST",
|
|
1050
|
+
body,
|
|
1051
|
+
headers: {
|
|
1052
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1053
|
+
},
|
|
1054
|
+
});
|
|
1055
|
+
await response.text(); // blank text? would be nice if it was something meaningful
|
|
1056
|
+
logger.log(
|
|
1057
|
+
"💁 Wrangler is configured with an OAuth token. The token has been successfully revoked"
|
|
1058
|
+
);
|
|
949
1059
|
}
|
|
950
1060
|
const body =
|
|
951
1061
|
`client_id=${encodeURIComponent(CLIENT_ID)}&` +
|
|
@@ -960,19 +1070,12 @@ export async function logout(): Promise<void> {
|
|
|
960
1070
|
},
|
|
961
1071
|
});
|
|
962
1072
|
await response.text(); // blank text? would be nice if it was something meaningful
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
);
|
|
966
|
-
// delete the file
|
|
967
|
-
await rm(path.join(os.homedir(), ".wrangler/config/default.toml"));
|
|
968
|
-
console.log(
|
|
969
|
-
`Removing ${os.homedir()}/.wrangler/config/default.toml.. success!`
|
|
970
|
-
);
|
|
1073
|
+
rmSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE));
|
|
1074
|
+
logger.log(`Successfully logged out.`);
|
|
971
1075
|
}
|
|
972
1076
|
|
|
973
|
-
export function listScopes(): void {
|
|
974
|
-
|
|
975
|
-
console.log("💁 Available scopes:");
|
|
1077
|
+
export function listScopes(message = "💁 Available scopes:"): void {
|
|
1078
|
+
logger.log(message);
|
|
976
1079
|
const data = ScopeKeys.map((scope: Scope) => ({
|
|
977
1080
|
Scope: scope,
|
|
978
1081
|
Description: Scopes[scope],
|
|
@@ -1037,6 +1140,11 @@ export async function getAccountId(
|
|
|
1037
1140
|
.join("\n")
|
|
1038
1141
|
);
|
|
1039
1142
|
}
|
|
1143
|
+
} else {
|
|
1144
|
+
if (!isInteractive)
|
|
1145
|
+
throw new Error(
|
|
1146
|
+
`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.`
|
|
1147
|
+
);
|
|
1040
1148
|
}
|
|
1041
1149
|
return accountId;
|
|
1042
1150
|
}
|
|
@@ -1063,3 +1171,23 @@ export function ChooseAccount(props: {
|
|
|
1063
1171
|
</>
|
|
1064
1172
|
);
|
|
1065
1173
|
}
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Ensure that a user is logged in, and a valid account_id is available.
|
|
1177
|
+
*/
|
|
1178
|
+
export async function requireAuth(config: {
|
|
1179
|
+
account_id?: string;
|
|
1180
|
+
}): Promise<string> {
|
|
1181
|
+
const isInteractive = process.stdin.isTTY;
|
|
1182
|
+
const loggedIn = await loginOrRefreshIfRequired(isInteractive);
|
|
1183
|
+
if (!loggedIn) {
|
|
1184
|
+
// didn't login, let's just quit
|
|
1185
|
+
throw new Error("Did not login, quitting...");
|
|
1186
|
+
}
|
|
1187
|
+
const accountId = config.account_id || (await getAccountId(isInteractive));
|
|
1188
|
+
if (!accountId) {
|
|
1189
|
+
throw new Error("No account id found, quitting...");
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
return accountId;
|
|
1193
|
+
}
|
package/src/whoami.tsx
CHANGED
|
@@ -2,10 +2,11 @@ import { Text, render } from "ink";
|
|
|
2
2
|
import Table from "ink-table";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
5
|
+
import { logger } from "./logger";
|
|
5
6
|
import { getAPIToken } from "./user";
|
|
6
7
|
|
|
7
8
|
export async function whoami() {
|
|
8
|
-
|
|
9
|
+
logger.log("Getting User settings...");
|
|
9
10
|
const user = await getUserInfo();
|
|
10
11
|
render(<WhoAmI user={user}></WhoAmI>);
|
|
11
12
|
}
|
package/src/worker.ts
CHANGED
|
@@ -75,7 +75,7 @@ interface CfKvNamespace {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* A binding to a wasm module (in service
|
|
78
|
+
* A binding to a wasm module (in service-worker format)
|
|
79
79
|
*/
|
|
80
80
|
|
|
81
81
|
interface CfWasmModuleBindings {
|
|
@@ -83,13 +83,21 @@ interface CfWasmModuleBindings {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* A binding to a text blob (in service
|
|
86
|
+
* A binding to a text blob (in service-worker format)
|
|
87
87
|
*/
|
|
88
88
|
|
|
89
89
|
interface CfTextBlobBindings {
|
|
90
90
|
[key: string]: string;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* A binding to a data blob (in service-worker format)
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
interface CfDataBlobBindings {
|
|
98
|
+
[key: string]: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
/**
|
|
94
102
|
* A Durable Object.
|
|
95
103
|
*/
|
|
@@ -137,7 +145,7 @@ export interface CfWorkerInit {
|
|
|
137
145
|
/**
|
|
138
146
|
* The list of additional modules.
|
|
139
147
|
*/
|
|
140
|
-
modules:
|
|
148
|
+
modules: CfModule[] | undefined;
|
|
141
149
|
/**
|
|
142
150
|
* All the bindings
|
|
143
151
|
*/
|
|
@@ -146,14 +154,15 @@ export interface CfWorkerInit {
|
|
|
146
154
|
kv_namespaces: CfKvNamespace[] | undefined;
|
|
147
155
|
wasm_modules: CfWasmModuleBindings | undefined;
|
|
148
156
|
text_blobs: CfTextBlobBindings | undefined;
|
|
157
|
+
data_blobs: CfDataBlobBindings | undefined;
|
|
149
158
|
durable_objects: { bindings: CfDurableObject[] } | undefined;
|
|
150
159
|
r2_buckets: CfR2Bucket[] | undefined;
|
|
151
160
|
unsafe: CfUnsafeBinding[] | undefined;
|
|
152
161
|
};
|
|
153
|
-
migrations:
|
|
162
|
+
migrations: CfDurableObjectMigrations | undefined;
|
|
154
163
|
compatibility_date: string | undefined;
|
|
155
|
-
compatibility_flags:
|
|
156
|
-
usage_model:
|
|
164
|
+
compatibility_flags: string[] | undefined;
|
|
165
|
+
usage_model: "bundled" | "unbound" | undefined;
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
export interface CfWorkerContext {
|