@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/LICENSE +18 -3
- package/dist/index.js +318 -239
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +316 -238
- 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/create-project/utils/apply-default-name.d.ts +7 -0
- package/dist/src/create-project/utils/apply-default-name.d.ts.map +1 -0
- package/dist/src/create-project/utils/get-project-name-from-pkg.d.ts +3 -0
- package/dist/src/create-project/utils/get-project-name-from-pkg.d.ts.map +1 -0
- 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 +6 -5
- package/dist/src/utils/tests/compress-files.test.d.ts +0 -2
- package/dist/src/utils/tests/compress-files.test.d.ts.map +0 -1
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.
|
|
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.
|
|
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.
|
|
205
|
-
tsconfig: "4.25.
|
|
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
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
logger.log(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
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
|
-
|
|
829
|
+
async function createProject$1(ctx, cloudApi, projectInput) {
|
|
596
830
|
const { logger } = ctx;
|
|
597
|
-
const
|
|
598
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
616
|
-
|
|
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$
|
|
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$
|
|
1066
|
+
const action$1 = async (ctx) => {
|
|
845
1067
|
const { getValidToken } = await tokenServiceFactory(ctx);
|
|
846
|
-
const
|
|
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$
|
|
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$
|
|
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",
|
|
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:
|
|
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$
|
|
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$
|
|
1213
|
+
action: action$2,
|
|
1136
1214
|
command
|
|
1137
1215
|
};
|
|
1138
1216
|
const cli = {
|