@strapi/cloud-cli 4.25.0 → 4.25.2

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/LICENSE CHANGED
@@ -2,7 +2,21 @@ Copyright (c) 2015-present Strapi Solutions SAS
2
2
 
3
3
  Portions of the Strapi software are licensed as follows:
4
4
 
5
- * All software that resides under an "ee/" directory (the “EE Software”), if that directory exists, is licensed under the license defined in "ee/LICENSE".
5
+ * All software that resides under an "ee/" directory (the “EE Software”), if that directory exists, is licensed under the license defined below.
6
+
7
+ Enterprise License
8
+
9
+ If you or the company you represent has entered into a written agreement referencing the Enterprise Edition of the Strapi source code available at
10
+ https://github.com/strapi/strapi, then such agreement applies to your use of the Enterprise Edition of the Strapi Software. If you or the company you
11
+ represent is using the Enterprise Edition of the Strapi Software in connection with a subscription to our cloud offering, then the agreement you have
12
+ agreed to with respect to our cloud offering and the licenses included in such agreement apply to your use of the Enterprise Edition of the Strapi Software.
13
+ Otherwise, the Strapi Enterprise Software License Agreement (found here https://strapi.io/enterprise-terms) applies to your use of the Enterprise Edition of the Strapi Software.
14
+
15
+ BY ACCESSING OR USING THE ENTERPRISE EDITION OF THE STRAPI SOFTWARE, YOU ARE AGREEING TO BE BOUND BY THE RELEVANT REFERENCED AGREEMENT.
16
+ IF YOU ARE NOT AUTHORIZED TO ACCEPT THESE TERMS ON BEHALF OF THE COMPANY YOU REPRESENT OR IF YOU DO NOT AGREE TO ALL OF THE RELEVANT TERMS AND CONDITIONS REFERENCED AND YOU
17
+ HAVE NOT OTHERWISE EXECUTED A WRITTEN AGREEMENT WITH STRAPI, YOU ARE NOT AUTHORIZED TO ACCESS OR USE OR ALLOW ANY USER TO ACCESS OR USE ANY PART OF
18
+ THE ENTERPRISE EDITION OF THE STRAPI SOFTWARE. YOUR ACCESS RIGHTS ARE CONDITIONAL ON YOUR CONSENT TO THE RELEVANT REFERENCED TERMS TO THE EXCLUSION OF ALL OTHER TERMS;
19
+ IF THE RELEVANT REFERENCED TERMS ARE CONSIDERED AN OFFER BY YOU, ACCEPTANCE IS EXPRESSLY LIMITED TO THE RELEVANT REFERENCED TERMS.
6
20
 
7
21
  * All software outside of the above-mentioned directories or restrictions above is available under the "MIT Expat" license as set forth below.
8
22
 
@@ -18,5 +32,6 @@ furnished to do so, subject to the following conditions:
18
32
  The above copyright notice and this permission notice shall be included in all
19
33
  copies or substantial portions of the Software.
20
34
 
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/dist/index.js CHANGED
@@ -194,7 +194,7 @@ async function saveLocalConfig(data) {
194
194
  await fse__default.default.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
195
195
  }
196
196
  const name = "@strapi/cloud-cli";
197
- const version = "4.24.5";
197
+ const version = "4.25.1";
198
198
  const description = "Commands to interact with the Strapi Cloud";
199
199
  const keywords = [
200
200
  "strapi",
@@ -238,7 +238,7 @@ const scripts = {
238
238
  watch: "pack-up watch"
239
239
  };
240
240
  const dependencies = {
241
- "@strapi/utils": "4.24.5",
241
+ "@strapi/utils": "4.25.1",
242
242
  axios: "1.6.0",
243
243
  chalk: "4.1.2",
244
244
  "cli-progress": "3.12.0",
@@ -263,8 +263,8 @@ const devDependencies = {
263
263
  "@types/cli-progress": "3.11.5",
264
264
  "@types/eventsource": "1.1.15",
265
265
  "@types/lodash": "^4.14.191",
266
- "eslint-config-custom": "4.24.5",
267
- tsconfig: "4.24.5"
266
+ "eslint-config-custom": "4.25.1",
267
+ tsconfig: "4.25.1"
268
268
  };
269
269
  const engines = {
270
270
  node: ">=18.0.0 <=20.x.x",
@@ -293,7 +293,7 @@ const packageJson = {
293
293
  engines
294
294
  };
295
295
  const VERSION = "v1";
296
- async function cloudApiFactory(token) {
296
+ async function cloudApiFactory({ logger }, token) {
297
297
  const localConfig = await getLocalConfig();
298
298
  const customHeaders = {
299
299
  "x-device-id": localConfig.deviceId,
@@ -346,8 +346,19 @@ async function cloudApiFactory(token) {
346
346
  getUserInfo() {
347
347
  return axiosCloudAPI.get("/user");
348
348
  },
349
- config() {
350
- return axiosCloudAPI.get("/config");
349
+ async config() {
350
+ try {
351
+ const response = await axiosCloudAPI.get("/config");
352
+ if (response.status !== 200) {
353
+ throw new Error("Error fetching cloud CLI config from the server.");
354
+ }
355
+ return response;
356
+ } catch (error) {
357
+ logger.debug(
358
+ "🥲 Oops! Couldn't retrieve the cloud CLI config from the server. Please try again."
359
+ );
360
+ throw error;
361
+ }
351
362
  },
352
363
  listProjects() {
353
364
  return axiosCloudAPI.get("/projects");
@@ -386,7 +397,7 @@ const strapiInfoSave = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defi
386
397
  }, Symbol.toStringTag, { value: "Module" }));
387
398
  let cliConfig;
388
399
  async function tokenServiceFactory({ logger }) {
389
- const cloudApiService = await cloudApiFactory();
400
+ const cloudApiService = await cloudApiFactory({ logger });
390
401
  async function saveToken(str) {
391
402
  const appConfig = await getLocalConfig();
392
403
  if (!appConfig) {
@@ -435,14 +446,17 @@ async function tokenServiceFactory({ logger }) {
435
446
  "There seems to be a problem with your login information. Please try logging in again."
436
447
  );
437
448
  }
449
+ return Promise.reject(new Error("Invalid token"));
438
450
  }
439
451
  return new Promise((resolve, reject) => {
440
452
  jwt__default.default.verify(idToken, getKey, (err) => {
441
453
  if (err) {
442
454
  reject(err);
443
- } else {
444
- resolve();
445
455
  }
456
+ if (decodedToken.payload.exp < Math.floor(Date.now() / 1e3)) {
457
+ reject(new Error("Token is expired"));
458
+ }
459
+ resolve();
446
460
  });
447
461
  });
448
462
  }
@@ -476,15 +490,15 @@ async function tokenServiceFactory({ logger }) {
476
490
  throw e;
477
491
  }
478
492
  }
479
- async function getValidToken() {
480
- const token = await retrieveToken();
481
- if (!token) {
482
- logger.log("No token found. Please login first.");
483
- return null;
484
- }
485
- if (!await isTokenValid(token)) {
486
- logger.log("Unable to proceed: Token is expired or not valid. Please login again.");
487
- return null;
493
+ async function getValidToken(ctx, loginAction2) {
494
+ let token = await retrieveToken();
495
+ while (!token || !await isTokenValid(token)) {
496
+ logger.log(
497
+ 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."
498
+ );
499
+ if (!await loginAction2(ctx))
500
+ return null;
501
+ token = await retrieveToken();
488
502
  }
489
503
  return token;
490
504
  }
@@ -618,17 +632,183 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
618
632
  local: strapiInfoSave,
619
633
  tokenServiceFactory
620
634
  }, Symbol.toStringTag, { value: "Module" }));
621
- async function handleError(ctx, error) {
635
+ const openModule$1 = import("open");
636
+ async function promptLogin(ctx) {
637
+ const response = await inquirer__default.default.prompt([
638
+ {
639
+ type: "confirm",
640
+ name: "login",
641
+ message: "Would you like to login?"
642
+ }
643
+ ]);
644
+ if (response.login) {
645
+ const loginSuccessful = await loginAction(ctx);
646
+ return loginSuccessful;
647
+ }
648
+ return false;
649
+ }
650
+ async function loginAction(ctx) {
651
+ const { logger } = ctx;
622
652
  const tokenService = await tokenServiceFactory(ctx);
653
+ const existingToken = await tokenService.retrieveToken();
654
+ const cloudApiService = await cloudApiFactory(ctx, existingToken || void 0);
655
+ const trackFailedLogin = async () => {
656
+ try {
657
+ await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
658
+ } catch (e) {
659
+ logger.debug("Failed to track failed login", e);
660
+ }
661
+ };
662
+ if (existingToken) {
663
+ const isTokenValid = await tokenService.isTokenValid(existingToken);
664
+ if (isTokenValid) {
665
+ try {
666
+ const userInfo = await cloudApiService.getUserInfo();
667
+ const { email } = userInfo.data.data;
668
+ if (email) {
669
+ logger.log(`You are already logged into your account (${email}).`);
670
+ } else {
671
+ logger.log("You are already logged in.");
672
+ }
673
+ logger.log(
674
+ "To access your dashboard, please copy and paste the following URL into your web browser:"
675
+ );
676
+ logger.log(chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects`));
677
+ return true;
678
+ } catch (e) {
679
+ logger.debug("Failed to fetch user info", e);
680
+ }
681
+ }
682
+ }
683
+ let cliConfig2;
684
+ try {
685
+ logger.info("🔌 Connecting to the Strapi Cloud API...");
686
+ const config = await cloudApiService.config();
687
+ cliConfig2 = config.data;
688
+ } catch (e) {
689
+ logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
690
+ logger.debug(e);
691
+ return false;
692
+ }
693
+ try {
694
+ await cloudApiService.track("willLoginAttempt", {});
695
+ } catch (e) {
696
+ logger.debug("Failed to track login attempt", e);
697
+ }
698
+ logger.debug("🔐 Creating device authentication request...", {
699
+ client_id: cliConfig2.clientId,
700
+ scope: cliConfig2.scope,
701
+ audience: cliConfig2.audience
702
+ });
703
+ const deviceAuthResponse = await axios__default.default.post(cliConfig2.deviceCodeAuthUrl, {
704
+ client_id: cliConfig2.clientId,
705
+ scope: cliConfig2.scope,
706
+ audience: cliConfig2.audience
707
+ }).catch((e) => {
708
+ logger.error("There was an issue with the authentication process. Please try again.");
709
+ if (e.message) {
710
+ logger.debug(e.message, e);
711
+ } else {
712
+ logger.debug(e);
713
+ }
714
+ });
715
+ openModule$1.then((open) => {
716
+ open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
717
+ logger.error("We encountered an issue opening the browser. Please try again later.");
718
+ logger.debug(e.message, e);
719
+ });
720
+ });
721
+ logger.log("If a browser tab does not open automatically, please follow the next steps:");
722
+ logger.log(
723
+ `1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
724
+ );
725
+ logger.log(
726
+ `2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
727
+ `
728
+ );
729
+ const tokenPayload = {
730
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
731
+ device_code: deviceAuthResponse.data.device_code,
732
+ client_id: cliConfig2.clientId
733
+ };
734
+ let isAuthenticated = false;
735
+ const authenticate = async () => {
736
+ const spinner = logger.spinner("Waiting for authentication");
737
+ spinner.start();
738
+ const spinnerFail = () => spinner.fail("Authentication failed!");
739
+ while (!isAuthenticated) {
740
+ try {
741
+ const tokenResponse = await axios__default.default.post(cliConfig2.tokenUrl, tokenPayload);
742
+ const authTokenData = tokenResponse.data;
743
+ if (tokenResponse.status === 200) {
744
+ try {
745
+ logger.debug("🔐 Validating token...");
746
+ await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
747
+ logger.debug("🔐 Token validation successful!");
748
+ } catch (e) {
749
+ logger.debug(e);
750
+ spinnerFail();
751
+ throw new Error("Unable to proceed: Token validation failed");
752
+ }
753
+ logger.debug("🔍 Fetching user information...");
754
+ const cloudApiServiceWithToken = await cloudApiFactory(ctx, authTokenData.access_token);
755
+ await cloudApiServiceWithToken.getUserInfo();
756
+ logger.debug("🔍 User information fetched successfully!");
757
+ try {
758
+ logger.debug("📝 Saving login information...");
759
+ await tokenService.saveToken(authTokenData.access_token);
760
+ logger.debug("📝 Login information saved successfully!");
761
+ isAuthenticated = true;
762
+ } catch (e) {
763
+ logger.error(
764
+ "There was a problem saving your login information. Please try logging in again."
765
+ );
766
+ logger.debug(e);
767
+ spinnerFail();
768
+ return false;
769
+ }
770
+ }
771
+ } catch (e) {
772
+ if (e.message === "Unable to proceed: Token validation failed") {
773
+ logger.error(
774
+ "There seems to be a problem with your login information. Please try logging in again."
775
+ );
776
+ spinnerFail();
777
+ await trackFailedLogin();
778
+ return false;
779
+ }
780
+ if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
781
+ logger.debug(e);
782
+ spinnerFail();
783
+ await trackFailedLogin();
784
+ return false;
785
+ }
786
+ await new Promise((resolve) => {
787
+ setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
788
+ });
789
+ }
790
+ }
791
+ spinner.succeed("Authentication successful!");
792
+ logger.log("You are now logged into Strapi Cloud.");
793
+ logger.log(
794
+ "To access your dashboard, please copy and paste the following URL into your web browser:"
795
+ );
796
+ logger.log(chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects`));
797
+ try {
798
+ await cloudApiService.track("didLogin", { loginMethod: "cli" });
799
+ } catch (e) {
800
+ logger.debug("Failed to track login", e);
801
+ }
802
+ };
803
+ await authenticate();
804
+ return isAuthenticated;
805
+ }
806
+ async function handleError(ctx, error) {
623
807
  const { logger } = ctx;
624
808
  logger.debug(error);
625
809
  if (error instanceof axios.AxiosError) {
626
810
  const errorMessage = typeof error.response?.data === "string" ? error.response.data : null;
627
811
  switch (error.response?.status) {
628
- case 401:
629
- logger.error("Your session has expired. Please log in again.");
630
- await tokenService.eraseToken();
631
- return;
632
812
  case 403:
633
813
  logger.error(
634
814
  errorMessage || "You do not have permission to create a project. Please contact support for assistance."
@@ -654,28 +834,44 @@ async function handleError(ctx, error) {
654
834
  "We encountered an issue while creating your project. Please try again in a moment. If the problem persists, contact support for assistance."
655
835
  );
656
836
  }
657
- const action$3 = async (ctx) => {
837
+ async function createProject$1(ctx, cloudApi, projectInput) {
658
838
  const { logger } = ctx;
659
- const { getValidToken } = await tokenServiceFactory(ctx);
660
- const token = await getValidToken();
839
+ const spinner = logger.spinner("Setting up your project...").start();
840
+ try {
841
+ const { data } = await cloudApi.createProject(projectInput);
842
+ await save({ project: data });
843
+ spinner.succeed("Project created successfully!");
844
+ return data;
845
+ } catch (e) {
846
+ spinner.fail("An error occurred while creating the project on Strapi Cloud.");
847
+ throw e;
848
+ }
849
+ }
850
+ const action$2 = async (ctx) => {
851
+ const { logger } = ctx;
852
+ const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
853
+ const token = await getValidToken(ctx, promptLogin);
661
854
  if (!token) {
662
855
  return;
663
856
  }
664
- const cloudApi = await cloudApiFactory(token);
857
+ const cloudApi = await cloudApiFactory(ctx, token);
665
858
  const { data: config } = await cloudApi.config();
666
859
  const { questions, defaults: defaultValues } = config.projectCreation;
667
860
  const projectAnswersDefaulted = fp.defaults(defaultValues);
668
861
  const projectAnswers = await inquirer__default.default.prompt(questions);
669
862
  const projectInput = projectAnswersDefaulted(projectAnswers);
670
- const spinner = logger.spinner("Setting up your project...").start();
671
863
  try {
672
- const { data } = await cloudApi.createProject(projectInput);
673
- await save({ project: data });
674
- spinner.succeed("Project created successfully!");
675
- return data;
864
+ return await createProject$1(ctx, cloudApi, projectInput);
676
865
  } catch (e) {
677
- spinner.fail("Failed to create project on Strapi Cloud.");
678
- await handleError(ctx, e);
866
+ if (e instanceof axios.AxiosError && e.response?.status === 401) {
867
+ logger.warn("Oops! Your session has expired. Please log in again to retry.");
868
+ await eraseToken();
869
+ if (await promptLogin(ctx)) {
870
+ return await createProject$1(ctx, cloudApi, projectInput);
871
+ }
872
+ } else {
873
+ await handleError(ctx, e);
874
+ }
679
875
  }
680
876
  };
681
877
  function notificationServiceFactory({ logger }) {
@@ -803,7 +999,7 @@ const buildLogsServiceFactory = ({ logger }) => {
803
999
  };
804
1000
  };
805
1001
  async function upload(ctx, project, token, maxProjectFileSize) {
806
- const cloudApi = await cloudApiFactory(token);
1002
+ const cloudApi = await cloudApiFactory(ctx, token);
807
1003
  try {
808
1004
  const storagePath = await getTmpStoragePath();
809
1005
  const projectFolder = path__namespace.default.resolve(process.cwd());
@@ -894,7 +1090,7 @@ async function getProject(ctx) {
894
1090
  const { project } = await retrieve();
895
1091
  if (!project) {
896
1092
  try {
897
- return await action$3(ctx);
1093
+ return await action$2(ctx);
898
1094
  } catch (e) {
899
1095
  ctx.logger.error("An error occurred while deploying the project. Please try again later.");
900
1096
  ctx.logger.debug(e);
@@ -903,10 +1099,9 @@ async function getProject(ctx) {
903
1099
  }
904
1100
  return project;
905
1101
  }
906
- const action$2 = async (ctx) => {
1102
+ const action$1 = async (ctx) => {
907
1103
  const { getValidToken } = await tokenServiceFactory(ctx);
908
- const cloudApiService = await cloudApiFactory();
909
- const token = await getValidToken();
1104
+ const token = await getValidToken(ctx, promptLogin);
910
1105
  if (!token) {
911
1106
  return;
912
1107
  }
@@ -914,6 +1109,7 @@ const action$2 = async (ctx) => {
914
1109
  if (!project) {
915
1110
  return;
916
1111
  }
1112
+ const cloudApiService = await cloudApiFactory(ctx);
917
1113
  try {
918
1114
  await cloudApiService.track("willDeployWithCLI", { projectInternalName: project.name });
919
1115
  } catch (e) {
@@ -978,183 +1174,27 @@ const runAction = (name2, action2) => (...args) => {
978
1174
  });
979
1175
  };
980
1176
  const command$3 = ({ command: command2, ctx }) => {
981
- command2.command("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$2)(ctx));
1177
+ command2.command("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));
982
1178
  };
983
1179
  const deployProject = {
984
1180
  name: "deploy-project",
985
1181
  description: "Deploy a Strapi Cloud project",
986
- action: action$2,
1182
+ action: action$1,
987
1183
  command: command$3
988
1184
  };
989
- const openModule = import("open");
990
- const action$1 = async (ctx) => {
991
- const { logger } = ctx;
992
- const tokenService = await tokenServiceFactory(ctx);
993
- const existingToken = await tokenService.retrieveToken();
994
- const cloudApiService = await cloudApiFactory(existingToken || void 0);
995
- const trackFailedLogin = async () => {
996
- try {
997
- await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
998
- } catch (e) {
999
- logger.debug("Failed to track failed login", e);
1000
- }
1001
- };
1002
- if (existingToken) {
1003
- const isTokenValid = await tokenService.isTokenValid(existingToken);
1004
- if (isTokenValid) {
1005
- try {
1006
- const userInfo = await cloudApiService.getUserInfo();
1007
- const { email } = userInfo.data.data;
1008
- if (email) {
1009
- logger.log(`You are already logged into your account (${email}).`);
1010
- } else {
1011
- logger.log("You are already logged in.");
1012
- }
1013
- logger.log(
1014
- "To access your dashboard, please copy and paste the following URL into your web browser:"
1015
- );
1016
- logger.log(chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects`));
1017
- return true;
1018
- } catch (e) {
1019
- logger.debug("Failed to fetch user info", e);
1020
- }
1021
- }
1022
- }
1023
- let cliConfig2;
1024
- try {
1025
- logger.info("🔌 Connecting to the Strapi Cloud API...");
1026
- const config = await cloudApiService.config();
1027
- cliConfig2 = config.data;
1028
- } catch (e) {
1029
- logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
1030
- logger.debug(e);
1031
- return false;
1032
- }
1033
- try {
1034
- await cloudApiService.track("willLoginAttempt", {});
1035
- } catch (e) {
1036
- logger.debug("Failed to track login attempt", e);
1037
- }
1038
- logger.debug("🔐 Creating device authentication request...", {
1039
- client_id: cliConfig2.clientId,
1040
- scope: cliConfig2.scope,
1041
- audience: cliConfig2.audience
1042
- });
1043
- const deviceAuthResponse = await axios__default.default.post(cliConfig2.deviceCodeAuthUrl, {
1044
- client_id: cliConfig2.clientId,
1045
- scope: cliConfig2.scope,
1046
- audience: cliConfig2.audience
1047
- }).catch((e) => {
1048
- logger.error("There was an issue with the authentication process. Please try again.");
1049
- if (e.message) {
1050
- logger.debug(e.message, e);
1051
- } else {
1052
- logger.debug(e);
1053
- }
1054
- });
1055
- openModule.then((open) => {
1056
- open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
1057
- logger.error("We encountered an issue opening the browser. Please try again later.");
1058
- logger.debug(e.message, e);
1059
- });
1060
- });
1061
- logger.log("If a browser tab does not open automatically, please follow the next steps:");
1062
- logger.log(
1063
- `1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
1064
- );
1065
- logger.log(
1066
- `2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
1067
- `
1068
- );
1069
- const tokenPayload = {
1070
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1071
- device_code: deviceAuthResponse.data.device_code,
1072
- client_id: cliConfig2.clientId
1073
- };
1074
- let isAuthenticated = false;
1075
- const authenticate = async () => {
1076
- const spinner = logger.spinner("Waiting for authentication");
1077
- spinner.start();
1078
- const spinnerFail = () => spinner.fail("Authentication failed!");
1079
- while (!isAuthenticated) {
1080
- try {
1081
- const tokenResponse = await axios__default.default.post(cliConfig2.tokenUrl, tokenPayload);
1082
- const authTokenData = tokenResponse.data;
1083
- if (tokenResponse.status === 200) {
1084
- try {
1085
- logger.debug("🔐 Validating token...");
1086
- await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
1087
- logger.debug("🔐 Token validation successful!");
1088
- } catch (e) {
1089
- logger.debug(e);
1090
- spinnerFail();
1091
- throw new Error("Unable to proceed: Token validation failed");
1092
- }
1093
- logger.debug("🔍 Fetching user information...");
1094
- const cloudApiServiceWithToken = await cloudApiFactory(authTokenData.access_token);
1095
- await cloudApiServiceWithToken.getUserInfo();
1096
- logger.debug("🔍 User information fetched successfully!");
1097
- try {
1098
- logger.debug("📝 Saving login information...");
1099
- await tokenService.saveToken(authTokenData.access_token);
1100
- logger.debug("📝 Login information saved successfully!");
1101
- isAuthenticated = true;
1102
- } catch (e) {
1103
- logger.error(
1104
- "There was a problem saving your login information. Please try logging in again."
1105
- );
1106
- logger.debug(e);
1107
- spinnerFail();
1108
- return false;
1109
- }
1110
- }
1111
- } catch (e) {
1112
- if (e.message === "Unable to proceed: Token validation failed") {
1113
- logger.error(
1114
- "There seems to be a problem with your login information. Please try logging in again."
1115
- );
1116
- spinnerFail();
1117
- await trackFailedLogin();
1118
- return false;
1119
- }
1120
- if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
1121
- logger.debug(e);
1122
- spinnerFail();
1123
- await trackFailedLogin();
1124
- return false;
1125
- }
1126
- await new Promise((resolve) => {
1127
- setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
1128
- });
1129
- }
1130
- }
1131
- spinner.succeed("Authentication successful!");
1132
- logger.log("You are now logged into Strapi Cloud.");
1133
- logger.log(
1134
- "To access your dashboard, please copy and paste the following URL into your web browser:"
1135
- );
1136
- logger.log(chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects`));
1137
- try {
1138
- await cloudApiService.track("didLogin", { loginMethod: "cli" });
1139
- } catch (e) {
1140
- logger.debug("Failed to track login", e);
1141
- }
1142
- };
1143
- await authenticate();
1144
- return isAuthenticated;
1145
- };
1146
1185
  const command$2 = ({ command: command2, ctx }) => {
1147
1186
  command2.command("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
1148
1187
  "after",
1149
1188
  "\nAfter running this command, you will be prompted to enter your authentication information."
1150
- ).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", action$1)(ctx));
1189
+ ).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", loginAction)(ctx));
1151
1190
  };
1152
1191
  const login = {
1153
1192
  name: "login",
1154
1193
  description: "Strapi Cloud Login",
1155
- action: action$1,
1194
+ action: loginAction,
1156
1195
  command: command$2
1157
1196
  };
1197
+ const openModule = import("open");
1158
1198
  const action = async (ctx) => {
1159
1199
  const { logger } = ctx;
1160
1200
  const { retrieveToken, eraseToken } = await tokenServiceFactory(ctx);
@@ -1163,9 +1203,21 @@ const action = async (ctx) => {
1163
1203
  logger.log("You're already logged out.");
1164
1204
  return;
1165
1205
  }
1166
- const cloudApiService = await cloudApiFactory(token);
1206
+ const cloudApiService = await cloudApiFactory(ctx, token);
1207
+ const config = await cloudApiService.config();
1208
+ const cliConfig2 = config.data;
1167
1209
  try {
1168
1210
  await eraseToken();
1211
+ openModule.then((open) => {
1212
+ open.default(
1213
+ `${cliConfig2.baseUrl}/oidc/logout?client_id=${encodeURIComponent(
1214
+ cliConfig2.clientId
1215
+ )}&logout_hint=${encodeURIComponent(token)}
1216
+ `
1217
+ ).catch((e) => {
1218
+ logger.debug(e.message, e);
1219
+ });
1220
+ });
1169
1221
  logger.log(
1170
1222
  "🔌 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."
1171
1223
  );
@@ -1189,12 +1241,12 @@ const logout = {
1189
1241
  command: command$1
1190
1242
  };
1191
1243
  const command = ({ command: command2, ctx }) => {
1192
- command2.command("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$3)(ctx));
1244
+ command2.command("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));
1193
1245
  };
1194
1246
  const createProject = {
1195
1247
  name: "create-project",
1196
1248
  description: "Create a new project",
1197
- action: action$3,
1249
+ action: action$2,
1198
1250
  command
1199
1251
  };
1200
1252
  const cli = {