@strapi/cloud-cli 4.25.1 → 4.25.3

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.mjs CHANGED
@@ -18,10 +18,11 @@ import jwt from "jsonwebtoken";
18
18
  import stringify from "fast-safe-stringify";
19
19
  import ora from "ora";
20
20
  import * as cliProgress from "cli-progress";
21
- import EventSource from "eventsource";
22
21
  import fs$1 from "fs/promises";
23
22
  import pkgUp from "pkg-up";
24
23
  import * as yup from "yup";
24
+ import _ from "lodash";
25
+ import EventSource from "eventsource";
25
26
  const apiConfig = {
26
27
  apiBaseUrl: env("STRAPI_CLI_CLOUD_API", "https://cloud-cli-api.strapi.io"),
27
28
  dashboardBaseUrl: env("STRAPI_CLI_CLOUD_DASHBOARD", "https://cloud.strapi.io")
@@ -132,7 +133,7 @@ async function saveLocalConfig(data) {
132
133
  await fse.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
133
134
  }
134
135
  const name = "@strapi/cloud-cli";
135
- const version = "4.25.0";
136
+ const version = "4.25.2";
136
137
  const description = "Commands to interact with the Strapi Cloud";
137
138
  const keywords = [
138
139
  "strapi",
@@ -173,10 +174,11 @@ const scripts = {
173
174
  build: "pack-up build",
174
175
  clean: "run -T rimraf ./dist",
175
176
  lint: "run -T eslint .",
177
+ "test:unit": "run -T jest",
176
178
  watch: "pack-up watch"
177
179
  };
178
180
  const dependencies = {
179
- "@strapi/utils": "4.25.0",
181
+ "@strapi/utils": "4.25.2",
180
182
  axios: "1.6.0",
181
183
  chalk: "4.1.2",
182
184
  "cli-progress": "3.12.0",
@@ -201,8 +203,8 @@ const devDependencies = {
201
203
  "@types/cli-progress": "3.11.5",
202
204
  "@types/eventsource": "1.1.15",
203
205
  "@types/lodash": "^4.14.191",
204
- "eslint-config-custom": "4.25.0",
205
- tsconfig: "4.25.0"
206
+ "eslint-config-custom": "4.25.2",
207
+ tsconfig: "4.25.2"
206
208
  };
207
209
  const engines = {
208
210
  node: ">=18.0.0 <=20.x.x",
@@ -231,7 +233,7 @@ const packageJson = {
231
233
  engines
232
234
  };
233
235
  const VERSION = "v1";
234
- async function cloudApiFactory(token) {
236
+ async function cloudApiFactory({ logger }, token) {
235
237
  const localConfig = await getLocalConfig();
236
238
  const customHeaders = {
237
239
  "x-device-id": localConfig.deviceId,
@@ -284,8 +286,19 @@ async function cloudApiFactory(token) {
284
286
  getUserInfo() {
285
287
  return axiosCloudAPI.get("/user");
286
288
  },
287
- config() {
288
- return axiosCloudAPI.get("/config");
289
+ async config() {
290
+ try {
291
+ const response = await axiosCloudAPI.get("/config");
292
+ if (response.status !== 200) {
293
+ throw new Error("Error fetching cloud CLI config from the server.");
294
+ }
295
+ return response;
296
+ } catch (error) {
297
+ logger.debug(
298
+ "🥲 Oops! Couldn't retrieve the cloud CLI config from the server. Please try again."
299
+ );
300
+ throw error;
301
+ }
289
302
  },
290
303
  listProjects() {
291
304
  return axiosCloudAPI.get("/projects");
@@ -324,7 +337,7 @@ const strapiInfoSave = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defi
324
337
  }, Symbol.toStringTag, { value: "Module" }));
325
338
  let cliConfig;
326
339
  async function tokenServiceFactory({ logger }) {
327
- const cloudApiService = await cloudApiFactory();
340
+ const cloudApiService = await cloudApiFactory({ logger });
328
341
  async function saveToken(str) {
329
342
  const appConfig = await getLocalConfig();
330
343
  if (!appConfig) {
@@ -373,14 +386,17 @@ async function tokenServiceFactory({ logger }) {
373
386
  "There seems to be a problem with your login information. Please try logging in again."
374
387
  );
375
388
  }
389
+ return Promise.reject(new Error("Invalid token"));
376
390
  }
377
391
  return new Promise((resolve, reject) => {
378
392
  jwt.verify(idToken, getKey, (err) => {
379
393
  if (err) {
380
394
  reject(err);
381
- } else {
382
- resolve();
383
395
  }
396
+ if (decodedToken.payload.exp < Math.floor(Date.now() / 1e3)) {
397
+ reject(new Error("Token is expired"));
398
+ }
399
+ resolve();
384
400
  });
385
401
  });
386
402
  }
@@ -414,15 +430,15 @@ async function tokenServiceFactory({ logger }) {
414
430
  throw e;
415
431
  }
416
432
  }
417
- async function getValidToken() {
418
- const token = await retrieveToken();
419
- if (!token) {
420
- logger.log("No token found. Please login first.");
421
- return null;
422
- }
423
- if (!await isTokenValid(token)) {
424
- logger.log("Unable to proceed: Token is expired or not valid. Please login again.");
425
- return null;
433
+ async function getValidToken(ctx, loginAction2) {
434
+ let token = await retrieveToken();
435
+ while (!token || !await isTokenValid(token)) {
436
+ logger.log(
437
+ 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."
438
+ );
439
+ if (!await loginAction2(ctx))
440
+ return null;
441
+ token = await retrieveToken();
426
442
  }
427
443
  return token;
428
444
  }
@@ -556,17 +572,235 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
556
572
  local: strapiInfoSave,
557
573
  tokenServiceFactory
558
574
  }, Symbol.toStringTag, { value: "Module" }));
559
- async function handleError(ctx, error) {
575
+ yup.object({
576
+ name: yup.string().required(),
577
+ exports: yup.lazy(
578
+ (value) => yup.object(
579
+ typeof value === "object" ? Object.entries(value).reduce((acc, [key, value2]) => {
580
+ if (typeof value2 === "object") {
581
+ acc[key] = yup.object({
582
+ types: yup.string().optional(),
583
+ source: yup.string().required(),
584
+ module: yup.string().optional(),
585
+ import: yup.string().required(),
586
+ require: yup.string().required(),
587
+ default: yup.string().required()
588
+ }).noUnknown(true);
589
+ } else {
590
+ acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
591
+ }
592
+ return acc;
593
+ }, {}) : void 0
594
+ ).optional()
595
+ )
596
+ });
597
+ const loadPkg = async ({ cwd, logger }) => {
598
+ const pkgPath = await pkgUp({ cwd });
599
+ if (!pkgPath) {
600
+ throw new Error("Could not find a package.json in the current directory");
601
+ }
602
+ const buffer = await fs$1.readFile(pkgPath);
603
+ const pkg = JSON.parse(buffer.toString());
604
+ logger.debug("Loaded package.json:", os.EOL, pkg);
605
+ return pkg;
606
+ };
607
+ async function getProjectNameFromPackageJson(ctx) {
608
+ try {
609
+ const packageJson2 = await loadPkg(ctx);
610
+ return packageJson2.name || "my-strapi-project";
611
+ } catch (e) {
612
+ return "my-strapi-project";
613
+ }
614
+ }
615
+ function applyDefaultName(newDefaultName, questions, defaultValues) {
616
+ const newDefaultValues = _.cloneDeep(defaultValues);
617
+ newDefaultValues.name = newDefaultName;
618
+ const newQuestions = questions.map((question) => {
619
+ const questionCopy = _.cloneDeep(question);
620
+ if (questionCopy.name === "name") {
621
+ questionCopy.default = newDefaultName;
622
+ }
623
+ return questionCopy;
624
+ });
625
+ return { newQuestions, newDefaultValues };
626
+ }
627
+ const openModule$1 = import("open");
628
+ async function promptLogin(ctx) {
629
+ const response = await inquirer.prompt([
630
+ {
631
+ type: "confirm",
632
+ name: "login",
633
+ message: "Would you like to login?"
634
+ }
635
+ ]);
636
+ if (response.login) {
637
+ const loginSuccessful = await loginAction(ctx);
638
+ return loginSuccessful;
639
+ }
640
+ return false;
641
+ }
642
+ async function loginAction(ctx) {
643
+ const { logger } = ctx;
560
644
  const tokenService = await tokenServiceFactory(ctx);
645
+ const existingToken = await tokenService.retrieveToken();
646
+ const cloudApiService = await cloudApiFactory(ctx, existingToken || void 0);
647
+ const trackFailedLogin = async () => {
648
+ try {
649
+ await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
650
+ } catch (e) {
651
+ logger.debug("Failed to track failed login", e);
652
+ }
653
+ };
654
+ if (existingToken) {
655
+ const isTokenValid = await tokenService.isTokenValid(existingToken);
656
+ if (isTokenValid) {
657
+ try {
658
+ const userInfo = await cloudApiService.getUserInfo();
659
+ const { email } = userInfo.data.data;
660
+ if (email) {
661
+ logger.log(`You are already logged into your account (${email}).`);
662
+ } else {
663
+ logger.log("You are already logged in.");
664
+ }
665
+ logger.log(
666
+ "To access your dashboard, please copy and paste the following URL into your web browser:"
667
+ );
668
+ logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
669
+ return true;
670
+ } catch (e) {
671
+ logger.debug("Failed to fetch user info", e);
672
+ }
673
+ }
674
+ }
675
+ let cliConfig2;
676
+ try {
677
+ logger.info("🔌 Connecting to the Strapi Cloud API...");
678
+ const config = await cloudApiService.config();
679
+ cliConfig2 = config.data;
680
+ } catch (e) {
681
+ logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
682
+ logger.debug(e);
683
+ return false;
684
+ }
685
+ try {
686
+ await cloudApiService.track("willLoginAttempt", {});
687
+ } catch (e) {
688
+ logger.debug("Failed to track login attempt", e);
689
+ }
690
+ logger.debug("🔐 Creating device authentication request...", {
691
+ client_id: cliConfig2.clientId,
692
+ scope: cliConfig2.scope,
693
+ audience: cliConfig2.audience
694
+ });
695
+ const deviceAuthResponse = await axios.post(cliConfig2.deviceCodeAuthUrl, {
696
+ client_id: cliConfig2.clientId,
697
+ scope: cliConfig2.scope,
698
+ audience: cliConfig2.audience
699
+ }).catch((e) => {
700
+ logger.error("There was an issue with the authentication process. Please try again.");
701
+ if (e.message) {
702
+ logger.debug(e.message, e);
703
+ } else {
704
+ logger.debug(e);
705
+ }
706
+ });
707
+ openModule$1.then((open) => {
708
+ open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
709
+ logger.error("We encountered an issue opening the browser. Please try again later.");
710
+ logger.debug(e.message, e);
711
+ });
712
+ });
713
+ logger.log("If a browser tab does not open automatically, please follow the next steps:");
714
+ logger.log(
715
+ `1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
716
+ );
717
+ logger.log(
718
+ `2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
719
+ `
720
+ );
721
+ const tokenPayload = {
722
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
723
+ device_code: deviceAuthResponse.data.device_code,
724
+ client_id: cliConfig2.clientId
725
+ };
726
+ let isAuthenticated = false;
727
+ const authenticate = async () => {
728
+ const spinner = logger.spinner("Waiting for authentication");
729
+ spinner.start();
730
+ const spinnerFail = () => spinner.fail("Authentication failed!");
731
+ while (!isAuthenticated) {
732
+ try {
733
+ const tokenResponse = await axios.post(cliConfig2.tokenUrl, tokenPayload);
734
+ const authTokenData = tokenResponse.data;
735
+ if (tokenResponse.status === 200) {
736
+ try {
737
+ logger.debug("🔐 Validating token...");
738
+ await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
739
+ logger.debug("🔐 Token validation successful!");
740
+ } catch (e) {
741
+ logger.debug(e);
742
+ spinnerFail();
743
+ throw new Error("Unable to proceed: Token validation failed");
744
+ }
745
+ logger.debug("🔍 Fetching user information...");
746
+ const cloudApiServiceWithToken = await cloudApiFactory(ctx, authTokenData.access_token);
747
+ await cloudApiServiceWithToken.getUserInfo();
748
+ logger.debug("🔍 User information fetched successfully!");
749
+ try {
750
+ logger.debug("📝 Saving login information...");
751
+ await tokenService.saveToken(authTokenData.access_token);
752
+ logger.debug("📝 Login information saved successfully!");
753
+ isAuthenticated = true;
754
+ } catch (e) {
755
+ logger.error(
756
+ "There was a problem saving your login information. Please try logging in again."
757
+ );
758
+ logger.debug(e);
759
+ spinnerFail();
760
+ return false;
761
+ }
762
+ }
763
+ } catch (e) {
764
+ if (e.message === "Unable to proceed: Token validation failed") {
765
+ logger.error(
766
+ "There seems to be a problem with your login information. Please try logging in again."
767
+ );
768
+ spinnerFail();
769
+ await trackFailedLogin();
770
+ return false;
771
+ }
772
+ if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
773
+ logger.debug(e);
774
+ spinnerFail();
775
+ await trackFailedLogin();
776
+ return false;
777
+ }
778
+ await new Promise((resolve) => {
779
+ setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
780
+ });
781
+ }
782
+ }
783
+ spinner.succeed("Authentication successful!");
784
+ logger.log("You are now logged into Strapi Cloud.");
785
+ logger.log(
786
+ "To access your dashboard, please copy and paste the following URL into your web browser:"
787
+ );
788
+ logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
789
+ try {
790
+ await cloudApiService.track("didLogin", { loginMethod: "cli" });
791
+ } catch (e) {
792
+ logger.debug("Failed to track login", e);
793
+ }
794
+ };
795
+ await authenticate();
796
+ return isAuthenticated;
797
+ }
798
+ async function handleError(ctx, error) {
561
799
  const { logger } = ctx;
562
800
  logger.debug(error);
563
801
  if (error instanceof AxiosError) {
564
802
  const errorMessage = typeof error.response?.data === "string" ? error.response.data : null;
565
803
  switch (error.response?.status) {
566
- case 401:
567
- logger.error("Your session has expired. Please log in again.");
568
- await tokenService.eraseToken();
569
- return;
570
804
  case 403:
571
805
  logger.error(
572
806
  errorMessage || "You do not have permission to create a project. Please contact support for assistance."
@@ -592,28 +826,48 @@ async function handleError(ctx, error) {
592
826
  "We encountered an issue while creating your project. Please try again in a moment. If the problem persists, contact support for assistance."
593
827
  );
594
828
  }
595
- const action$3 = async (ctx) => {
829
+ async function createProject$1(ctx, cloudApi, projectInput) {
596
830
  const { logger } = ctx;
597
- const { getValidToken } = await tokenServiceFactory(ctx);
598
- const token = await getValidToken();
831
+ const spinner = logger.spinner("Setting up your project...").start();
832
+ try {
833
+ const { data } = await cloudApi.createProject(projectInput);
834
+ await save({ project: data });
835
+ spinner.succeed("Project created successfully!");
836
+ return data;
837
+ } catch (e) {
838
+ spinner.fail("An error occurred while creating the project on Strapi Cloud.");
839
+ throw e;
840
+ }
841
+ }
842
+ const action$2 = async (ctx) => {
843
+ const { logger } = ctx;
844
+ const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
845
+ const token = await getValidToken(ctx, promptLogin);
599
846
  if (!token) {
600
847
  return;
601
848
  }
602
- const cloudApi = await cloudApiFactory(token);
849
+ const cloudApi = await cloudApiFactory(ctx, token);
603
850
  const { data: config } = await cloudApi.config();
604
- const { questions, defaults: defaultValues } = config.projectCreation;
851
+ const { newQuestions: questions, newDefaultValues: defaultValues } = applyDefaultName(
852
+ await getProjectNameFromPackageJson(ctx),
853
+ config.projectCreation.questions,
854
+ config.projectCreation.defaults
855
+ );
605
856
  const projectAnswersDefaulted = defaults(defaultValues);
606
857
  const projectAnswers = await inquirer.prompt(questions);
607
858
  const projectInput = projectAnswersDefaulted(projectAnswers);
608
- const spinner = logger.spinner("Setting up your project...").start();
609
859
  try {
610
- const { data } = await cloudApi.createProject(projectInput);
611
- await save({ project: data });
612
- spinner.succeed("Project created successfully!");
613
- return data;
860
+ return await createProject$1(ctx, cloudApi, projectInput);
614
861
  } catch (e) {
615
- spinner.fail("Failed to create project on Strapi Cloud.");
616
- await handleError(ctx, e);
862
+ if (e instanceof AxiosError && e.response?.status === 401) {
863
+ logger.warn("Oops! Your session has expired. Please log in again to retry.");
864
+ await eraseToken();
865
+ if (await promptLogin(ctx)) {
866
+ return await createProject$1(ctx, cloudApi, projectInput);
867
+ }
868
+ } else {
869
+ await handleError(ctx, e);
870
+ }
617
871
  }
618
872
  };
619
873
  function notificationServiceFactory({ logger }) {
@@ -647,38 +901,6 @@ function notificationServiceFactory({ logger }) {
647
901
  };
648
902
  };
649
903
  }
650
- yup.object({
651
- name: yup.string().required(),
652
- exports: yup.lazy(
653
- (value) => yup.object(
654
- typeof value === "object" ? Object.entries(value).reduce((acc, [key, value2]) => {
655
- if (typeof value2 === "object") {
656
- acc[key] = yup.object({
657
- types: yup.string().optional(),
658
- source: yup.string().required(),
659
- module: yup.string().optional(),
660
- import: yup.string().required(),
661
- require: yup.string().required(),
662
- default: yup.string().required()
663
- }).noUnknown(true);
664
- } else {
665
- acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
666
- }
667
- return acc;
668
- }, {}) : void 0
669
- ).optional()
670
- )
671
- });
672
- const loadPkg = async ({ cwd, logger }) => {
673
- const pkgPath = await pkgUp({ cwd });
674
- if (!pkgPath) {
675
- throw new Error("Could not find a package.json in the current directory");
676
- }
677
- const buffer = await fs$1.readFile(pkgPath);
678
- const pkg = JSON.parse(buffer.toString());
679
- logger.debug("Loaded package.json:", os.EOL, pkg);
680
- return pkg;
681
- };
682
904
  const buildLogsServiceFactory = ({ logger }) => {
683
905
  return async (url, token, cliConfig2) => {
684
906
  const CONN_TIMEOUT = Number(cliConfig2.buildLogsConnectionTimeout);
@@ -741,7 +963,7 @@ const buildLogsServiceFactory = ({ logger }) => {
741
963
  };
742
964
  };
743
965
  async function upload(ctx, project, token, maxProjectFileSize) {
744
- const cloudApi = await cloudApiFactory(token);
966
+ const cloudApi = await cloudApiFactory(ctx, token);
745
967
  try {
746
968
  const storagePath = await getTmpStoragePath();
747
969
  const projectFolder = path__default.resolve(process.cwd());
@@ -832,7 +1054,7 @@ async function getProject(ctx) {
832
1054
  const { project } = await retrieve();
833
1055
  if (!project) {
834
1056
  try {
835
- return await action$3(ctx);
1057
+ return await action$2(ctx);
836
1058
  } catch (e) {
837
1059
  ctx.logger.error("An error occurred while deploying the project. Please try again later.");
838
1060
  ctx.logger.debug(e);
@@ -841,10 +1063,9 @@ async function getProject(ctx) {
841
1063
  }
842
1064
  return project;
843
1065
  }
844
- const action$2 = async (ctx) => {
1066
+ const action$1 = async (ctx) => {
845
1067
  const { getValidToken } = await tokenServiceFactory(ctx);
846
- const cloudApiService = await cloudApiFactory();
847
- const token = await getValidToken();
1068
+ const token = await getValidToken(ctx, promptLogin);
848
1069
  if (!token) {
849
1070
  return;
850
1071
  }
@@ -852,6 +1073,7 @@ const action$2 = async (ctx) => {
852
1073
  if (!project) {
853
1074
  return;
854
1075
  }
1076
+ const cloudApiService = await cloudApiFactory(ctx);
855
1077
  try {
856
1078
  await cloudApiService.track("willDeployWithCLI", { projectInternalName: project.name });
857
1079
  } catch (e) {
@@ -916,183 +1138,27 @@ const runAction = (name2, action2) => (...args) => {
916
1138
  });
917
1139
  };
918
1140
  const command$3 = ({ command: command2, ctx }) => {
919
- 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));
1141
+ 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));
920
1142
  };
921
1143
  const deployProject = {
922
1144
  name: "deploy-project",
923
1145
  description: "Deploy a Strapi Cloud project",
924
- action: action$2,
1146
+ action: action$1,
925
1147
  command: command$3
926
1148
  };
927
- const openModule = import("open");
928
- const action$1 = async (ctx) => {
929
- const { logger } = ctx;
930
- const tokenService = await tokenServiceFactory(ctx);
931
- const existingToken = await tokenService.retrieveToken();
932
- const cloudApiService = await cloudApiFactory(existingToken || void 0);
933
- const trackFailedLogin = async () => {
934
- try {
935
- await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
936
- } catch (e) {
937
- logger.debug("Failed to track failed login", e);
938
- }
939
- };
940
- if (existingToken) {
941
- const isTokenValid = await tokenService.isTokenValid(existingToken);
942
- if (isTokenValid) {
943
- try {
944
- const userInfo = await cloudApiService.getUserInfo();
945
- const { email } = userInfo.data.data;
946
- if (email) {
947
- logger.log(`You are already logged into your account (${email}).`);
948
- } else {
949
- logger.log("You are already logged in.");
950
- }
951
- logger.log(
952
- "To access your dashboard, please copy and paste the following URL into your web browser:"
953
- );
954
- logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
955
- return true;
956
- } catch (e) {
957
- logger.debug("Failed to fetch user info", e);
958
- }
959
- }
960
- }
961
- let cliConfig2;
962
- try {
963
- logger.info("🔌 Connecting to the Strapi Cloud API...");
964
- const config = await cloudApiService.config();
965
- cliConfig2 = config.data;
966
- } catch (e) {
967
- logger.error("🥲 Oops! Something went wrong while logging you in. Please try again.");
968
- logger.debug(e);
969
- return false;
970
- }
971
- try {
972
- await cloudApiService.track("willLoginAttempt", {});
973
- } catch (e) {
974
- logger.debug("Failed to track login attempt", e);
975
- }
976
- logger.debug("🔐 Creating device authentication request...", {
977
- client_id: cliConfig2.clientId,
978
- scope: cliConfig2.scope,
979
- audience: cliConfig2.audience
980
- });
981
- const deviceAuthResponse = await axios.post(cliConfig2.deviceCodeAuthUrl, {
982
- client_id: cliConfig2.clientId,
983
- scope: cliConfig2.scope,
984
- audience: cliConfig2.audience
985
- }).catch((e) => {
986
- logger.error("There was an issue with the authentication process. Please try again.");
987
- if (e.message) {
988
- logger.debug(e.message, e);
989
- } else {
990
- logger.debug(e);
991
- }
992
- });
993
- openModule.then((open) => {
994
- open.default(deviceAuthResponse.data.verification_uri_complete).catch((e) => {
995
- logger.error("We encountered an issue opening the browser. Please try again later.");
996
- logger.debug(e.message, e);
997
- });
998
- });
999
- logger.log("If a browser tab does not open automatically, please follow the next steps:");
1000
- logger.log(
1001
- `1. Open this url in your device: ${deviceAuthResponse.data.verification_uri_complete}`
1002
- );
1003
- logger.log(
1004
- `2. Enter the following code: ${deviceAuthResponse.data.user_code} and confirm to login.
1005
- `
1006
- );
1007
- const tokenPayload = {
1008
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1009
- device_code: deviceAuthResponse.data.device_code,
1010
- client_id: cliConfig2.clientId
1011
- };
1012
- let isAuthenticated = false;
1013
- const authenticate = async () => {
1014
- const spinner = logger.spinner("Waiting for authentication");
1015
- spinner.start();
1016
- const spinnerFail = () => spinner.fail("Authentication failed!");
1017
- while (!isAuthenticated) {
1018
- try {
1019
- const tokenResponse = await axios.post(cliConfig2.tokenUrl, tokenPayload);
1020
- const authTokenData = tokenResponse.data;
1021
- if (tokenResponse.status === 200) {
1022
- try {
1023
- logger.debug("🔐 Validating token...");
1024
- await tokenService.validateToken(authTokenData.id_token, cliConfig2.jwksUrl);
1025
- logger.debug("🔐 Token validation successful!");
1026
- } catch (e) {
1027
- logger.debug(e);
1028
- spinnerFail();
1029
- throw new Error("Unable to proceed: Token validation failed");
1030
- }
1031
- logger.debug("🔍 Fetching user information...");
1032
- const cloudApiServiceWithToken = await cloudApiFactory(authTokenData.access_token);
1033
- await cloudApiServiceWithToken.getUserInfo();
1034
- logger.debug("🔍 User information fetched successfully!");
1035
- try {
1036
- logger.debug("📝 Saving login information...");
1037
- await tokenService.saveToken(authTokenData.access_token);
1038
- logger.debug("📝 Login information saved successfully!");
1039
- isAuthenticated = true;
1040
- } catch (e) {
1041
- logger.error(
1042
- "There was a problem saving your login information. Please try logging in again."
1043
- );
1044
- logger.debug(e);
1045
- spinnerFail();
1046
- return false;
1047
- }
1048
- }
1049
- } catch (e) {
1050
- if (e.message === "Unable to proceed: Token validation failed") {
1051
- logger.error(
1052
- "There seems to be a problem with your login information. Please try logging in again."
1053
- );
1054
- spinnerFail();
1055
- await trackFailedLogin();
1056
- return false;
1057
- }
1058
- if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
1059
- logger.debug(e);
1060
- spinnerFail();
1061
- await trackFailedLogin();
1062
- return false;
1063
- }
1064
- await new Promise((resolve) => {
1065
- setTimeout(resolve, deviceAuthResponse.data.interval * 1e3);
1066
- });
1067
- }
1068
- }
1069
- spinner.succeed("Authentication successful!");
1070
- logger.log("You are now logged into Strapi Cloud.");
1071
- logger.log(
1072
- "To access your dashboard, please copy and paste the following URL into your web browser:"
1073
- );
1074
- logger.log(chalk.underline(`${apiConfig.dashboardBaseUrl}/projects`));
1075
- try {
1076
- await cloudApiService.track("didLogin", { loginMethod: "cli" });
1077
- } catch (e) {
1078
- logger.debug("Failed to track login", e);
1079
- }
1080
- };
1081
- await authenticate();
1082
- return isAuthenticated;
1083
- };
1084
1149
  const command$2 = ({ command: command2, ctx }) => {
1085
1150
  command2.command("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
1086
1151
  "after",
1087
1152
  "\nAfter running this command, you will be prompted to enter your authentication information."
1088
- ).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", action$1)(ctx));
1153
+ ).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", loginAction)(ctx));
1089
1154
  };
1090
1155
  const login = {
1091
1156
  name: "login",
1092
1157
  description: "Strapi Cloud Login",
1093
- action: action$1,
1158
+ action: loginAction,
1094
1159
  command: command$2
1095
1160
  };
1161
+ const openModule = import("open");
1096
1162
  const action = async (ctx) => {
1097
1163
  const { logger } = ctx;
1098
1164
  const { retrieveToken, eraseToken } = await tokenServiceFactory(ctx);
@@ -1101,9 +1167,21 @@ const action = async (ctx) => {
1101
1167
  logger.log("You're already logged out.");
1102
1168
  return;
1103
1169
  }
1104
- const cloudApiService = await cloudApiFactory(token);
1170
+ const cloudApiService = await cloudApiFactory(ctx, token);
1171
+ const config = await cloudApiService.config();
1172
+ const cliConfig2 = config.data;
1105
1173
  try {
1106
1174
  await eraseToken();
1175
+ openModule.then((open) => {
1176
+ open.default(
1177
+ `${cliConfig2.baseUrl}/oidc/logout?client_id=${encodeURIComponent(
1178
+ cliConfig2.clientId
1179
+ )}&logout_hint=${encodeURIComponent(token)}
1180
+ `
1181
+ ).catch((e) => {
1182
+ logger.debug(e.message, e);
1183
+ });
1184
+ });
1107
1185
  logger.log(
1108
1186
  "🔌 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."
1109
1187
  );
@@ -1127,12 +1205,12 @@ const logout = {
1127
1205
  command: command$1
1128
1206
  };
1129
1207
  const command = ({ command: command2, ctx }) => {
1130
- 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));
1208
+ 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));
1131
1209
  };
1132
1210
  const createProject = {
1133
1211
  name: "create-project",
1134
1212
  description: "Create a new project",
1135
- action: action$3,
1213
+ action: action$2,
1136
1214
  command
1137
1215
  };
1138
1216
  const cli = {