@strapi/cloud-cli 5.0.0-rc.0 → 5.0.0-rc.10
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/dist/index.js +258 -206
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +258 -206
- package/dist/index.mjs.map +1 -1
- package/dist/src/create-project/action.d.ts +1 -1
- package/dist/src/create-project/action.d.ts.map +1 -1
- package/dist/src/deploy-project/action.d.ts.map +1 -1
- package/dist/src/login/action.d.ts +2 -2
- package/dist/src/login/action.d.ts.map +1 -1
- package/dist/src/logout/action.d.ts.map +1 -1
- package/dist/src/services/cli-api.d.ts +4 -2
- package/dist/src/services/cli-api.d.ts.map +1 -1
- package/dist/src/services/token.d.ts +1 -1
- package/dist/src/services/token.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -133,7 +133,7 @@ async function saveLocalConfig(data) {
|
|
|
133
133
|
await fse.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
|
|
134
134
|
}
|
|
135
135
|
const name = "@strapi/cloud-cli";
|
|
136
|
-
const version = "5.0.0-
|
|
136
|
+
const version = "5.0.0-rc.9";
|
|
137
137
|
const description = "Commands to interact with the Strapi Cloud";
|
|
138
138
|
const keywords = [
|
|
139
139
|
"strapi",
|
|
@@ -177,14 +177,14 @@ const scripts = {
|
|
|
177
177
|
watch: "pack-up watch"
|
|
178
178
|
};
|
|
179
179
|
const dependencies = {
|
|
180
|
-
"@strapi/utils": "
|
|
181
|
-
axios: "1.6.
|
|
180
|
+
"@strapi/utils": "workspace:*",
|
|
181
|
+
axios: "1.6.8",
|
|
182
182
|
chalk: "4.1.2",
|
|
183
183
|
"cli-progress": "3.12.0",
|
|
184
184
|
commander: "8.3.0",
|
|
185
185
|
eventsource: "2.0.2",
|
|
186
186
|
"fast-safe-stringify": "2.1.1",
|
|
187
|
-
"fs-extra": "
|
|
187
|
+
"fs-extra": "11.2.0",
|
|
188
188
|
inquirer: "8.2.5",
|
|
189
189
|
jsonwebtoken: "9.0.0",
|
|
190
190
|
"jwks-rsa": "3.1.0",
|
|
@@ -202,8 +202,8 @@ const devDependencies = {
|
|
|
202
202
|
"@types/cli-progress": "3.11.5",
|
|
203
203
|
"@types/eventsource": "1.1.15",
|
|
204
204
|
"@types/lodash": "^4.14.191",
|
|
205
|
-
"eslint-config-custom": "
|
|
206
|
-
tsconfig: "
|
|
205
|
+
"eslint-config-custom": "workspace:*",
|
|
206
|
+
tsconfig: "workspace:*"
|
|
207
207
|
};
|
|
208
208
|
const engines = {
|
|
209
209
|
node: ">=18.0.0 <=20.x.x",
|
|
@@ -232,7 +232,7 @@ const packageJson = {
|
|
|
232
232
|
engines
|
|
233
233
|
};
|
|
234
234
|
const VERSION = "v1";
|
|
235
|
-
async function cloudApiFactory(token) {
|
|
235
|
+
async function cloudApiFactory({ logger }, token) {
|
|
236
236
|
const localConfig = await getLocalConfig();
|
|
237
237
|
const customHeaders = {
|
|
238
238
|
"x-device-id": localConfig.deviceId,
|
|
@@ -285,8 +285,19 @@ async function cloudApiFactory(token) {
|
|
|
285
285
|
getUserInfo() {
|
|
286
286
|
return axiosCloudAPI.get("/user");
|
|
287
287
|
},
|
|
288
|
-
config() {
|
|
289
|
-
|
|
288
|
+
async config() {
|
|
289
|
+
try {
|
|
290
|
+
const response = await axiosCloudAPI.get("/config");
|
|
291
|
+
if (response.status !== 200) {
|
|
292
|
+
throw new Error("Error fetching cloud CLI config from the server.");
|
|
293
|
+
}
|
|
294
|
+
return response;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
logger.debug(
|
|
297
|
+
"🥲 Oops! Couldn't retrieve the cloud CLI config from the server. Please try again."
|
|
298
|
+
);
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
290
301
|
},
|
|
291
302
|
listProjects() {
|
|
292
303
|
return axiosCloudAPI.get("/projects");
|
|
@@ -325,7 +336,7 @@ const strapiInfoSave = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defi
|
|
|
325
336
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
326
337
|
let cliConfig;
|
|
327
338
|
async function tokenServiceFactory({ logger }) {
|
|
328
|
-
const cloudApiService = await cloudApiFactory();
|
|
339
|
+
const cloudApiService = await cloudApiFactory({ logger });
|
|
329
340
|
async function saveToken(str) {
|
|
330
341
|
const appConfig = await getLocalConfig();
|
|
331
342
|
if (!appConfig) {
|
|
@@ -374,14 +385,17 @@ async function tokenServiceFactory({ logger }) {
|
|
|
374
385
|
"There seems to be a problem with your login information. Please try logging in again."
|
|
375
386
|
);
|
|
376
387
|
}
|
|
388
|
+
return Promise.reject(new Error("Invalid token"));
|
|
377
389
|
}
|
|
378
390
|
return new Promise((resolve, reject) => {
|
|
379
391
|
jwt.verify(idToken, getKey, (err) => {
|
|
380
392
|
if (err) {
|
|
381
393
|
reject(err);
|
|
382
|
-
} else {
|
|
383
|
-
resolve();
|
|
384
394
|
}
|
|
395
|
+
if (decodedToken.payload.exp < Math.floor(Date.now() / 1e3)) {
|
|
396
|
+
reject(new Error("Token is expired"));
|
|
397
|
+
}
|
|
398
|
+
resolve();
|
|
385
399
|
});
|
|
386
400
|
});
|
|
387
401
|
}
|
|
@@ -415,15 +429,15 @@ async function tokenServiceFactory({ logger }) {
|
|
|
415
429
|
throw e;
|
|
416
430
|
}
|
|
417
431
|
}
|
|
418
|
-
async function getValidToken() {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
logger.log(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
432
|
+
async function getValidToken(ctx, loginAction2) {
|
|
433
|
+
let token = await retrieveToken();
|
|
434
|
+
while (!token || !await isTokenValid(token)) {
|
|
435
|
+
logger.log(
|
|
436
|
+
token ? "Oops! Your token seems expired or invalid. Please login again." : "We couldn't find a valid token. You need to be logged in to use this feature."
|
|
437
|
+
);
|
|
438
|
+
if (!await loginAction2(ctx))
|
|
439
|
+
return null;
|
|
440
|
+
token = await retrieveToken();
|
|
427
441
|
}
|
|
428
442
|
return token;
|
|
429
443
|
}
|
|
@@ -557,17 +571,183 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
557
571
|
local: strapiInfoSave,
|
|
558
572
|
tokenServiceFactory
|
|
559
573
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
560
|
-
|
|
574
|
+
const openModule$1 = import("open");
|
|
575
|
+
async function promptLogin(ctx) {
|
|
576
|
+
const response = await inquirer.prompt([
|
|
577
|
+
{
|
|
578
|
+
type: "confirm",
|
|
579
|
+
name: "login",
|
|
580
|
+
message: "Would you like to login?"
|
|
581
|
+
}
|
|
582
|
+
]);
|
|
583
|
+
if (response.login) {
|
|
584
|
+
const loginSuccessful = await loginAction(ctx);
|
|
585
|
+
return loginSuccessful;
|
|
586
|
+
}
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
async function loginAction(ctx) {
|
|
590
|
+
const { logger } = ctx;
|
|
561
591
|
const tokenService = await tokenServiceFactory(ctx);
|
|
592
|
+
const existingToken = await tokenService.retrieveToken();
|
|
593
|
+
const cloudApiService = await cloudApiFactory(ctx, existingToken || void 0);
|
|
594
|
+
const trackFailedLogin = async () => {
|
|
595
|
+
try {
|
|
596
|
+
await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
|
|
597
|
+
} catch (e) {
|
|
598
|
+
logger.debug("Failed to track failed login", e);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
if (existingToken) {
|
|
602
|
+
const isTokenValid = await tokenService.isTokenValid(existingToken);
|
|
603
|
+
if (isTokenValid) {
|
|
604
|
+
try {
|
|
605
|
+
const userInfo = await cloudApiService.getUserInfo();
|
|
606
|
+
const { email } = userInfo.data.data;
|
|
607
|
+
if (email) {
|
|
608
|
+
logger.log(`You are already logged into your account (${email}).`);
|
|
609
|
+
} else {
|
|
610
|
+
logger.log("You are already logged in.");
|
|
611
|
+
}
|
|
612
|
+
logger.log(
|
|
613
|
+
"To access your dashboard, please copy and paste the following URL into your web browser:"
|
|
614
|
+
);
|
|
615
|
+
logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
|
|
616
|
+
return true;
|
|
617
|
+
} catch (e) {
|
|
618
|
+
logger.debug("Failed to fetch user info", e);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
let cliConfig2;
|
|
623
|
+
try {
|
|
624
|
+
logger.info("🔌 Connecting to the Strapi Cloud API...");
|
|
625
|
+
const config = await cloudApiService.config();
|
|
626
|
+
cliConfig2 = config.data;
|
|
627
|
+
} catch (e) {
|
|
628
|
+
logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
|
|
629
|
+
logger.debug(e);
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
await cloudApiService.track("willLoginAttempt", {});
|
|
634
|
+
} catch (e) {
|
|
635
|
+
logger.debug("Failed to track login attempt", e);
|
|
636
|
+
}
|
|
637
|
+
logger.debug("🔐 Creating device authentication request...", {
|
|
638
|
+
client_id: cliConfig2.clientId,
|
|
639
|
+
scope: cliConfig2.scope,
|
|
640
|
+
audience: cliConfig2.audience
|
|
641
|
+
});
|
|
642
|
+
const deviceAuthResponse = await axios.post(cliConfig2.deviceCodeAuthUrl, {
|
|
643
|
+
client_id: cliConfig2.clientId,
|
|
644
|
+
scope: cliConfig2.scope,
|
|
645
|
+
audience: cliConfig2.audience
|
|
646
|
+
}).catch((e) => {
|
|
647
|
+
logger.error("There was an issue with the authentication process. Please try again.");
|
|
648
|
+
if (e.message) {
|
|
649
|
+
logger.debug(e.message, e);
|
|
650
|
+
} else {
|
|
651
|
+
logger.debug(e);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
openModule$1.then((open) => {
|
|
655
|
+
open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
|
|
656
|
+
logger.error("We encountered an issue opening the browser. Please try again later.");
|
|
657
|
+
logger.debug(e.message, e);
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
logger.log("If a browser tab does not open automatically, please follow the next steps:");
|
|
661
|
+
logger.log(
|
|
662
|
+
`1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
|
|
663
|
+
);
|
|
664
|
+
logger.log(
|
|
665
|
+
`2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
|
|
666
|
+
`
|
|
667
|
+
);
|
|
668
|
+
const tokenPayload = {
|
|
669
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
670
|
+
device_code: deviceAuthResponse.data.device_code,
|
|
671
|
+
client_id: cliConfig2.clientId
|
|
672
|
+
};
|
|
673
|
+
let isAuthenticated = false;
|
|
674
|
+
const authenticate = async () => {
|
|
675
|
+
const spinner = logger.spinner("Waiting for authentication");
|
|
676
|
+
spinner.start();
|
|
677
|
+
const spinnerFail = () => spinner.fail("Authentication failed!");
|
|
678
|
+
while (!isAuthenticated) {
|
|
679
|
+
try {
|
|
680
|
+
const tokenResponse = await axios.post(cliConfig2.tokenUrl, tokenPayload);
|
|
681
|
+
const authTokenData = tokenResponse.data;
|
|
682
|
+
if (tokenResponse.status === 200) {
|
|
683
|
+
try {
|
|
684
|
+
logger.debug("🔐 Validating token...");
|
|
685
|
+
await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
|
|
686
|
+
logger.debug("🔐 Token validation successful!");
|
|
687
|
+
} catch (e) {
|
|
688
|
+
logger.debug(e);
|
|
689
|
+
spinnerFail();
|
|
690
|
+
throw new Error("Unable to proceed: Token validation failed");
|
|
691
|
+
}
|
|
692
|
+
logger.debug("🔍 Fetching user information...");
|
|
693
|
+
const cloudApiServiceWithToken = await cloudApiFactory(ctx, authTokenData.access_token);
|
|
694
|
+
await cloudApiServiceWithToken.getUserInfo();
|
|
695
|
+
logger.debug("🔍 User information fetched successfully!");
|
|
696
|
+
try {
|
|
697
|
+
logger.debug("📝 Saving login information...");
|
|
698
|
+
await tokenService.saveToken(authTokenData.access_token);
|
|
699
|
+
logger.debug("📝 Login information saved successfully!");
|
|
700
|
+
isAuthenticated = true;
|
|
701
|
+
} catch (e) {
|
|
702
|
+
logger.error(
|
|
703
|
+
"There was a problem saving your login information. Please try logging in again."
|
|
704
|
+
);
|
|
705
|
+
logger.debug(e);
|
|
706
|
+
spinnerFail();
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} catch (e) {
|
|
711
|
+
if (e.message === "Unable to proceed: Token validation failed") {
|
|
712
|
+
logger.error(
|
|
713
|
+
"There seems to be a problem with your login information. Please try logging in again."
|
|
714
|
+
);
|
|
715
|
+
spinnerFail();
|
|
716
|
+
await trackFailedLogin();
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
|
|
720
|
+
logger.debug(e);
|
|
721
|
+
spinnerFail();
|
|
722
|
+
await trackFailedLogin();
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
await new Promise((resolve) => {
|
|
726
|
+
setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
spinner.succeed("Authentication successful!");
|
|
731
|
+
logger.log("You are now logged into Strapi Cloud.");
|
|
732
|
+
logger.log(
|
|
733
|
+
"To access your dashboard, please copy and paste the following URL into your web browser:"
|
|
734
|
+
);
|
|
735
|
+
logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
|
|
736
|
+
try {
|
|
737
|
+
await cloudApiService.track("didLogin", { loginMethod: "cli" });
|
|
738
|
+
} catch (e) {
|
|
739
|
+
logger.debug("Failed to track login", e);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
await authenticate();
|
|
743
|
+
return isAuthenticated;
|
|
744
|
+
}
|
|
745
|
+
async function handleError(ctx, error) {
|
|
562
746
|
const { logger } = ctx;
|
|
563
747
|
logger.debug(error);
|
|
564
748
|
if (error instanceof AxiosError) {
|
|
565
749
|
const errorMessage = typeof error.response?.data === "string" ? error.response.data : null;
|
|
566
750
|
switch (error.response?.status) {
|
|
567
|
-
case 401:
|
|
568
|
-
logger.error("Your session has expired. Please log in again.");
|
|
569
|
-
await tokenService.eraseToken();
|
|
570
|
-
return;
|
|
571
751
|
case 403:
|
|
572
752
|
logger.error(
|
|
573
753
|
errorMessage || "You do not have permission to create a project. Please contact support for assistance."
|
|
@@ -593,28 +773,44 @@ async function handleError(ctx, error) {
|
|
|
593
773
|
"We encountered an issue while creating your project. Please try again in a moment. If the problem persists, contact support for assistance."
|
|
594
774
|
);
|
|
595
775
|
}
|
|
596
|
-
|
|
776
|
+
async function createProject$1(ctx, cloudApi, projectInput) {
|
|
597
777
|
const { logger } = ctx;
|
|
598
|
-
const
|
|
599
|
-
|
|
778
|
+
const spinner = logger.spinner("Setting up your project...").start();
|
|
779
|
+
try {
|
|
780
|
+
const { data } = await cloudApi.createProject(projectInput);
|
|
781
|
+
await save({ project: data });
|
|
782
|
+
spinner.succeed("Project created successfully!");
|
|
783
|
+
return data;
|
|
784
|
+
} catch (e) {
|
|
785
|
+
spinner.fail("An error occurred while creating the project on Strapi Cloud.");
|
|
786
|
+
throw e;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
const action$2 = async (ctx) => {
|
|
790
|
+
const { logger } = ctx;
|
|
791
|
+
const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
|
|
792
|
+
const token = await getValidToken(ctx, promptLogin);
|
|
600
793
|
if (!token) {
|
|
601
794
|
return;
|
|
602
795
|
}
|
|
603
|
-
const cloudApi = await cloudApiFactory(token);
|
|
796
|
+
const cloudApi = await cloudApiFactory(ctx, token);
|
|
604
797
|
const { data: config } = await cloudApi.config();
|
|
605
798
|
const { questions, defaults: defaultValues } = config.projectCreation;
|
|
606
799
|
const projectAnswersDefaulted = defaults(defaultValues);
|
|
607
800
|
const projectAnswers = await inquirer.prompt(questions);
|
|
608
801
|
const projectInput = projectAnswersDefaulted(projectAnswers);
|
|
609
|
-
const spinner = logger.spinner("Setting up your project...").start();
|
|
610
802
|
try {
|
|
611
|
-
|
|
612
|
-
await save({ project: data });
|
|
613
|
-
spinner.succeed("Project created successfully!");
|
|
614
|
-
return data;
|
|
803
|
+
return await createProject$1(ctx, cloudApi, projectInput);
|
|
615
804
|
} catch (e) {
|
|
616
|
-
|
|
617
|
-
|
|
805
|
+
if (e instanceof AxiosError && e.response?.status === 401) {
|
|
806
|
+
logger.warn("Oops! Your session has expired. Please log in again to retry.");
|
|
807
|
+
await eraseToken();
|
|
808
|
+
if (await promptLogin(ctx)) {
|
|
809
|
+
return await createProject$1(ctx, cloudApi, projectInput);
|
|
810
|
+
}
|
|
811
|
+
} else {
|
|
812
|
+
await handleError(ctx, e);
|
|
813
|
+
}
|
|
618
814
|
}
|
|
619
815
|
};
|
|
620
816
|
function notificationServiceFactory({ logger }) {
|
|
@@ -745,7 +941,7 @@ const buildLogsServiceFactory = ({ logger }) => {
|
|
|
745
941
|
};
|
|
746
942
|
};
|
|
747
943
|
async function upload(ctx, project, token, maxProjectFileSize) {
|
|
748
|
-
const cloudApi = await cloudApiFactory(token);
|
|
944
|
+
const cloudApi = await cloudApiFactory(ctx, token);
|
|
749
945
|
try {
|
|
750
946
|
const storagePath = await getTmpStoragePath();
|
|
751
947
|
const projectFolder = path__default.resolve(process.cwd());
|
|
@@ -836,7 +1032,7 @@ async function getProject(ctx) {
|
|
|
836
1032
|
const { project } = await retrieve();
|
|
837
1033
|
if (!project) {
|
|
838
1034
|
try {
|
|
839
|
-
return await action$
|
|
1035
|
+
return await action$2(ctx);
|
|
840
1036
|
} catch (e) {
|
|
841
1037
|
ctx.logger.error("An error occurred while deploying the project. Please try again later.");
|
|
842
1038
|
ctx.logger.debug(e);
|
|
@@ -845,10 +1041,9 @@ async function getProject(ctx) {
|
|
|
845
1041
|
}
|
|
846
1042
|
return project;
|
|
847
1043
|
}
|
|
848
|
-
const action$
|
|
1044
|
+
const action$1 = async (ctx) => {
|
|
849
1045
|
const { getValidToken } = await tokenServiceFactory(ctx);
|
|
850
|
-
const
|
|
851
|
-
const token = await getValidToken();
|
|
1046
|
+
const token = await getValidToken(ctx, promptLogin);
|
|
852
1047
|
if (!token) {
|
|
853
1048
|
return;
|
|
854
1049
|
}
|
|
@@ -856,6 +1051,7 @@ const action$2 = async (ctx) => {
|
|
|
856
1051
|
if (!project) {
|
|
857
1052
|
return;
|
|
858
1053
|
}
|
|
1054
|
+
const cloudApiService = await cloudApiFactory(ctx);
|
|
859
1055
|
try {
|
|
860
1056
|
await cloudApiService.track("willDeployWithCLI", { projectInternalName: project.name });
|
|
861
1057
|
} catch (e) {
|
|
@@ -920,183 +1116,27 @@ const runAction = (name2, action2) => (...args) => {
|
|
|
920
1116
|
});
|
|
921
1117
|
};
|
|
922
1118
|
const command$3 = ({ ctx }) => {
|
|
923
|
-
return createCommand("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("deploy", action$
|
|
1119
|
+
return createCommand("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("deploy", action$1)(ctx));
|
|
924
1120
|
};
|
|
925
1121
|
const deployProject = {
|
|
926
1122
|
name: "deploy-project",
|
|
927
1123
|
description: "Deploy a Strapi Cloud project",
|
|
928
|
-
action: action$
|
|
1124
|
+
action: action$1,
|
|
929
1125
|
command: command$3
|
|
930
1126
|
};
|
|
931
|
-
const openModule = import("open");
|
|
932
|
-
const action$1 = async (ctx) => {
|
|
933
|
-
const { logger } = ctx;
|
|
934
|
-
const tokenService = await tokenServiceFactory(ctx);
|
|
935
|
-
const existingToken = await tokenService.retrieveToken();
|
|
936
|
-
const cloudApiService = await cloudApiFactory(existingToken || void 0);
|
|
937
|
-
const trackFailedLogin = async () => {
|
|
938
|
-
try {
|
|
939
|
-
await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
|
|
940
|
-
} catch (e) {
|
|
941
|
-
logger.debug("Failed to track failed login", e);
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
if (existingToken) {
|
|
945
|
-
const isTokenValid = await tokenService.isTokenValid(existingToken);
|
|
946
|
-
if (isTokenValid) {
|
|
947
|
-
try {
|
|
948
|
-
const userInfo = await cloudApiService.getUserInfo();
|
|
949
|
-
const { email } = userInfo.data.data;
|
|
950
|
-
if (email) {
|
|
951
|
-
logger.log(`You are already logged into your account (${email}).`);
|
|
952
|
-
} else {
|
|
953
|
-
logger.log("You are already logged in.");
|
|
954
|
-
}
|
|
955
|
-
logger.log(
|
|
956
|
-
"To access your dashboard, please copy and paste the following URL into your web browser:"
|
|
957
|
-
);
|
|
958
|
-
logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
|
|
959
|
-
return true;
|
|
960
|
-
} catch (e) {
|
|
961
|
-
logger.debug("Failed to fetch user info", e);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
let cliConfig2;
|
|
966
|
-
try {
|
|
967
|
-
logger.info("🔌 Connecting to the Strapi Cloud API...");
|
|
968
|
-
const config = await cloudApiService.config();
|
|
969
|
-
cliConfig2 = config.data;
|
|
970
|
-
} catch (e) {
|
|
971
|
-
logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
|
|
972
|
-
logger.debug(e);
|
|
973
|
-
return false;
|
|
974
|
-
}
|
|
975
|
-
try {
|
|
976
|
-
await cloudApiService.track("willLoginAttempt", {});
|
|
977
|
-
} catch (e) {
|
|
978
|
-
logger.debug("Failed to track login attempt", e);
|
|
979
|
-
}
|
|
980
|
-
logger.debug("🔐 Creating device authentication request...", {
|
|
981
|
-
client_id: cliConfig2.clientId,
|
|
982
|
-
scope: cliConfig2.scope,
|
|
983
|
-
audience: cliConfig2.audience
|
|
984
|
-
});
|
|
985
|
-
const deviceAuthResponse = await axios.post(cliConfig2.deviceCodeAuthUrl, {
|
|
986
|
-
client_id: cliConfig2.clientId,
|
|
987
|
-
scope: cliConfig2.scope,
|
|
988
|
-
audience: cliConfig2.audience
|
|
989
|
-
}).catch((e) => {
|
|
990
|
-
logger.error("There was an issue with the authentication process. Please try again.");
|
|
991
|
-
if (e.message) {
|
|
992
|
-
logger.debug(e.message, e);
|
|
993
|
-
} else {
|
|
994
|
-
logger.debug(e);
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
openModule.then((open) => {
|
|
998
|
-
open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
|
|
999
|
-
logger.error("We encountered an issue opening the browser. Please try again later.");
|
|
1000
|
-
logger.debug(e.message, e);
|
|
1001
|
-
});
|
|
1002
|
-
});
|
|
1003
|
-
logger.log("If a browser tab does not open automatically, please follow the next steps:");
|
|
1004
|
-
logger.log(
|
|
1005
|
-
`1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
|
|
1006
|
-
);
|
|
1007
|
-
logger.log(
|
|
1008
|
-
`2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
|
|
1009
|
-
`
|
|
1010
|
-
);
|
|
1011
|
-
const tokenPayload = {
|
|
1012
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
1013
|
-
device_code: deviceAuthResponse.data.device_code,
|
|
1014
|
-
client_id: cliConfig2.clientId
|
|
1015
|
-
};
|
|
1016
|
-
let isAuthenticated = false;
|
|
1017
|
-
const authenticate = async () => {
|
|
1018
|
-
const spinner = logger.spinner("Waiting for authentication");
|
|
1019
|
-
spinner.start();
|
|
1020
|
-
const spinnerFail = () => spinner.fail("Authentication failed!");
|
|
1021
|
-
while (!isAuthenticated) {
|
|
1022
|
-
try {
|
|
1023
|
-
const tokenResponse = await axios.post(cliConfig2.tokenUrl, tokenPayload);
|
|
1024
|
-
const authTokenData = tokenResponse.data;
|
|
1025
|
-
if (tokenResponse.status === 200) {
|
|
1026
|
-
try {
|
|
1027
|
-
logger.debug("🔐 Validating token...");
|
|
1028
|
-
await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
|
|
1029
|
-
logger.debug("🔐 Token validation successful!");
|
|
1030
|
-
} catch (e) {
|
|
1031
|
-
logger.debug(e);
|
|
1032
|
-
spinnerFail();
|
|
1033
|
-
throw new Error("Unable to proceed: Token validation failed");
|
|
1034
|
-
}
|
|
1035
|
-
logger.debug("🔍 Fetching user information...");
|
|
1036
|
-
const cloudApiServiceWithToken = await cloudApiFactory(authTokenData.access_token);
|
|
1037
|
-
await cloudApiServiceWithToken.getUserInfo();
|
|
1038
|
-
logger.debug("🔍 User information fetched successfully!");
|
|
1039
|
-
try {
|
|
1040
|
-
logger.debug("📝 Saving login information...");
|
|
1041
|
-
await tokenService.saveToken(authTokenData.access_token);
|
|
1042
|
-
logger.debug("📝 Login information saved successfully!");
|
|
1043
|
-
isAuthenticated = true;
|
|
1044
|
-
} catch (e) {
|
|
1045
|
-
logger.error(
|
|
1046
|
-
"There was a problem saving your login information. Please try logging in again."
|
|
1047
|
-
);
|
|
1048
|
-
logger.debug(e);
|
|
1049
|
-
spinnerFail();
|
|
1050
|
-
return false;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
} catch (e) {
|
|
1054
|
-
if (e.message === "Unable to proceed: Token validation failed") {
|
|
1055
|
-
logger.error(
|
|
1056
|
-
"There seems to be a problem with your login information. Please try logging in again."
|
|
1057
|
-
);
|
|
1058
|
-
spinnerFail();
|
|
1059
|
-
await trackFailedLogin();
|
|
1060
|
-
return false;
|
|
1061
|
-
}
|
|
1062
|
-
if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
|
|
1063
|
-
logger.debug(e);
|
|
1064
|
-
spinnerFail();
|
|
1065
|
-
await trackFailedLogin();
|
|
1066
|
-
return false;
|
|
1067
|
-
}
|
|
1068
|
-
await new Promise((resolve) => {
|
|
1069
|
-
setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
spinner.succeed("Authentication successful!");
|
|
1074
|
-
logger.log("You are now logged into Strapi Cloud.");
|
|
1075
|
-
logger.log(
|
|
1076
|
-
"To access your dashboard, please copy and paste the following URL into your web browser:"
|
|
1077
|
-
);
|
|
1078
|
-
logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
|
|
1079
|
-
try {
|
|
1080
|
-
await cloudApiService.track("didLogin", { loginMethod: "cli" });
|
|
1081
|
-
} catch (e) {
|
|
1082
|
-
logger.debug("Failed to track login", e);
|
|
1083
|
-
}
|
|
1084
|
-
};
|
|
1085
|
-
await authenticate();
|
|
1086
|
-
return isAuthenticated;
|
|
1087
|
-
};
|
|
1088
1127
|
const command$2 = ({ ctx }) => {
|
|
1089
1128
|
return createCommand("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
|
|
1090
1129
|
"after",
|
|
1091
1130
|
"\nAfter running this command, you will be prompted to enter your authentication information."
|
|
1092
|
-
).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login",
|
|
1131
|
+
).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", loginAction)(ctx));
|
|
1093
1132
|
};
|
|
1094
1133
|
const login = {
|
|
1095
1134
|
name: "login",
|
|
1096
1135
|
description: "Strapi Cloud Login",
|
|
1097
|
-
action:
|
|
1136
|
+
action: loginAction,
|
|
1098
1137
|
command: command$2
|
|
1099
1138
|
};
|
|
1139
|
+
const openModule = import("open");
|
|
1100
1140
|
const action = async (ctx) => {
|
|
1101
1141
|
const { logger } = ctx;
|
|
1102
1142
|
const { retrieveToken, eraseToken } = await tokenServiceFactory(ctx);
|
|
@@ -1105,9 +1145,21 @@ const action = async (ctx) => {
|
|
|
1105
1145
|
logger.log("You're already logged out.");
|
|
1106
1146
|
return;
|
|
1107
1147
|
}
|
|
1108
|
-
const cloudApiService = await cloudApiFactory(token);
|
|
1148
|
+
const cloudApiService = await cloudApiFactory(ctx, token);
|
|
1149
|
+
const config = await cloudApiService.config();
|
|
1150
|
+
const cliConfig2 = config.data;
|
|
1109
1151
|
try {
|
|
1110
1152
|
await eraseToken();
|
|
1153
|
+
openModule.then((open) => {
|
|
1154
|
+
open.default(
|
|
1155
|
+
`${cliConfig2.baseUrl}/oidc/logout?client_id=${encodeURIComponent(
|
|
1156
|
+
cliConfig2.clientId
|
|
1157
|
+
)}&logout_hint=${encodeURIComponent(token)}
|
|
1158
|
+
`
|
|
1159
|
+
).catch((e) => {
|
|
1160
|
+
logger.debug(e.message, e);
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1111
1163
|
logger.log(
|
|
1112
1164
|
"🔌 You have been logged out from the CLI. If you are on a shared computer, please make sure to log out from the Strapi Cloud Dashboard as well."
|
|
1113
1165
|
);
|
|
@@ -1131,12 +1183,12 @@ const logout = {
|
|
|
1131
1183
|
command: command$1
|
|
1132
1184
|
};
|
|
1133
1185
|
const command = ({ ctx }) => {
|
|
1134
|
-
return createCommand("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$
|
|
1186
|
+
return createCommand("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$2)(ctx));
|
|
1135
1187
|
};
|
|
1136
1188
|
const createProject = {
|
|
1137
1189
|
name: "create-project",
|
|
1138
1190
|
description: "Create a new project",
|
|
1139
|
-
action: action$
|
|
1191
|
+
action: action$2,
|
|
1140
1192
|
command
|
|
1141
1193
|
};
|
|
1142
1194
|
const cli = {
|