shoplazza-cli 1.0.13 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/bin/shoplazza +5 -3
- package/lib/app/api/cli.js +225 -0
- package/lib/app/api/openapi.js +121 -0
- package/lib/app/api/partnerOpenapi.js +104 -0
- package/lib/app/bin/index.js +20 -0
- package/lib/app/bin/javy/javy-arm-linux-v5.0.1 +0 -0
- package/lib/app/bin/javy/javy-arm-macos-v5.0.1 +0 -0
- package/lib/app/bin/javy/javy-x86_64-linux-v5.0.1 +0 -0
- package/lib/app/bin/javy/javy-x86_64-macos-v5.0.1 +0 -0
- package/lib/app/bin/javy/javy-x86_64-windows-v5.0.1 +0 -0
- package/lib/app/commands/config/actions/link.js +189 -0
- package/lib/app/commands/config/actions/use.js +40 -0
- package/lib/app/commands/config/index.js +25 -0
- package/lib/app/commands/config/link.js +11 -0
- package/lib/app/commands/config/use.js +11 -0
- package/lib/app/commands/deploy/actions/deploy.js +196 -0
- package/lib/app/commands/deploy/index.js +11 -0
- package/lib/app/commands/dev/actions/dev.js +206 -0
- package/lib/app/commands/dev/index.js +11 -0
- package/lib/app/commands/generate/actions/extension.js +97 -0
- package/lib/app/commands/generate/actions/generateCheckout.js +58 -0
- package/lib/app/commands/generate/actions/generateFunction.js +56 -0
- package/lib/app/commands/generate/actions/generateTheme.js +128 -0
- package/lib/app/commands/generate/extension.js +11 -0
- package/lib/app/commands/generate/index.js +22 -0
- package/lib/app/commands/index.js +82 -0
- package/lib/app/commands/info/actions/info.js +168 -0
- package/lib/app/commands/info/index.js +11 -0
- package/lib/app/commands/init/actions/init.js +176 -0
- package/lib/app/commands/init/index.js +14 -0
- package/lib/app/commands/versions/actions/list.js +210 -0
- package/lib/app/commands/versions/index.js +22 -0
- package/lib/app/commands/versions/list.js +14 -0
- package/lib/app/constant/code.js +7 -0
- package/lib/app/constant/color.js +18 -0
- package/lib/app/constant/extension.js +16 -0
- package/lib/app/constant/host.js +23 -0
- package/lib/app/constant/sso.js +7 -0
- package/lib/app/index.js +4 -25
- package/lib/app/services/auth/config.js +33 -0
- package/lib/app/services/auth/index.js +9 -0
- package/lib/app/services/auth/oauth-server.js +70 -0
- package/lib/app/services/auth/partner-token.js +45 -0
- package/lib/app/services/auth/sso-token.js +69 -0
- package/lib/app/services/auth/store-token.js +100 -0
- package/lib/app/services/auth/url-builder.js +23 -0
- package/lib/app/services/config/index.js +41 -0
- package/lib/app/services/devServer/app.js +76 -0
- package/lib/app/services/devServer/index.js +103 -0
- package/lib/app/services/devServer/middleware/hmacValidatorMiddleWare.js +20 -0
- package/lib/app/services/devServer/middleware/index.js +5 -0
- package/lib/app/services/devServer/tunnel/index.js +43 -0
- package/lib/app/services/devServer/tunnel/providers/cloudflare.js +364 -0
- package/lib/app/services/devServer/tunnel/providers/ngrok.js +70 -0
- package/lib/app/services/devServer/utils/index.js +5 -0
- package/lib/app/services/devServer/utils/secureCompare.js +5 -0
- package/lib/app/services/devServer/views/app.ejs +133 -0
- package/lib/app/services/extension-build/buildCheckout.js +47 -0
- package/lib/app/services/extension-build/buildFunction.js +57 -0
- package/lib/app/services/extension-build/buildTheme.js +100 -0
- package/lib/app/services/extension-build/index.js +23 -0
- package/lib/app/services/extension-build/plugins/vite-plugin-add-extension-id.js +26 -0
- package/lib/app/services/extension-build/plugins/vite-plugin-transform-extension-html.js +207 -0
- package/lib/app/services/extension-diff/index.js +132 -0
- package/lib/app/services/extension-upsert/index.js +21 -0
- package/lib/app/services/extension-upsert/upsertCheckout.js +44 -0
- package/lib/app/services/extension-upsert/upsertFunction.js +52 -0
- package/lib/app/services/extension-upsert/upsertTheme.js +113 -0
- package/lib/app/services/oss/index.js +45 -0
- package/lib/app/services/partner/index.js +52 -0
- package/lib/app/store/base-store.js +37 -0
- package/lib/app/store/config-store.js +55 -0
- package/lib/app/store/config.js +21 -0
- package/lib/app/store/index.js +14 -0
- package/lib/app/store/install-store.js +41 -0
- package/lib/app/store/sso-store.js +55 -0
- package/lib/app/utils/asyncPool.js +42 -0
- package/lib/app/utils/debug/index.js +16 -0
- package/lib/app/utils/env.js +24 -0
- package/lib/app/utils/error.js +20 -0
- package/lib/app/utils/git.js +20 -0
- package/lib/app/utils/json.js +27 -0
- package/lib/app/utils/path.js +33 -0
- package/lib/app/utils/platform.js +37 -0
- package/lib/app/utils/request/cli.js +72 -0
- package/lib/app/utils/request/debug.js +13 -0
- package/lib/app/utils/request/openapi.js +67 -0
- package/lib/app/utils/request/partnerOpenapi.js +47 -0
- package/lib/app/utils/toml.js +56 -0
- package/lib/app/utils/views/message.js +68 -0
- package/lib/app/utils/views/select.js +36 -0
- package/lib/app/utils/withTempDir.js +55 -0
- package/lib/checkout/api.js +2 -0
- package/lib/function/bin/javy/javy-arm-macos-v5.0.1 +0 -0
- package/lib/oss.js +5 -2
- package/lib/theme-extension/index.js +29 -0
- package/lib/utils/config.js +1 -1
- package/package.json +12 -1
- package/examples/checkout-extension/README.md +0 -19
- package/examples/checkout-extension/extension.config.js +0 -4
- package/examples/checkout-extension/extensions/add-shipping-desc/extension.json +0 -10
- package/examples/checkout-extension/extensions/add-shipping-desc/src/index.js +0 -7
- package/examples/checkout-extension/extensions/ext-1/extension.json +0 -10
- package/examples/checkout-extension/extensions/ext-1/src/content.html +0 -3
- package/examples/checkout-extension/extensions/ext-1/src/index.html +0 -5
- package/examples/checkout-extension/extensions/ext-1/src/index.js +0 -11
- package/examples/checkout-extension/extensions/ext-1/src/script.html +0 -3
- package/examples/checkout-extension/extensions/ext-1/src/style.html +0 -3
- package/examples/checkout-extension/extensions/product-list/extension.json +0 -10
- package/examples/checkout-extension/extensions/product-list/src/index.js +0 -5
- package/examples/checkout-extension/extensions/rewrite-navigate/extension.json +0 -10
- package/examples/checkout-extension/extensions/rewrite-navigate/src/content.html +0 -38
- package/examples/checkout-extension/extensions/rewrite-navigate/src/index.html +0 -5
- package/examples/checkout-extension/extensions/rewrite-navigate/src/index.js +0 -12
- package/examples/checkout-extension/extensions/rewrite-navigate/src/script.html +0 -26
- package/examples/checkout-extension/extensions/rewrite-navigate/src/style.html +0 -23
- package/examples/checkout-extension/package-lock.json +0 -121
- package/examples/checkout-extension/package.json +0 -17
- /package/lib/{app → theme-extension}/api/index.js +0 -0
- /package/lib/{app → theme-extension}/commands/build.js +0 -0
- /package/lib/{app → theme-extension}/commands/connect.js +0 -0
- /package/lib/{app → theme-extension}/commands/create.js +0 -0
- /package/lib/{app → theme-extension}/commands/deploy.js +0 -0
- /package/lib/{app → theme-extension}/commands/list.js +0 -0
- /package/lib/{app → theme-extension}/commands/release.js +0 -0
- /package/lib/{app → theme-extension}/commands/serve.js +0 -0
- /package/lib/{app → theme-extension}/commands/versions.js +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/README.md +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/extension.config.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/package.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/assets/index.css +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/assets-manifest.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/blocks/index.liquid +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ar-SA.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/de-DE.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/en-US.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/es-ES.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/fr-FR.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/id-ID.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/it-IT.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ja-JP.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ko-KR.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/nl-NL.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/pl-PL.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/pt-PT.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ru-RU.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/th-TH.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/zh-CN.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/zh-TW.json +0 -0
- /package/lib/{app → theme-extension}/template/basic-app/theme-app/snippets/index.liquid +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/README.md +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/extension.config.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/package.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/assets-manifest.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/blocks/index.liquid +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ar-SA.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/de-DE.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/en-US.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/es-ES.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/fr-FR.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/id-ID.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/it-IT.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ja-JP.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ko-KR.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/nl-NL.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/pl-PL.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/pt-PT.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ru-RU.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/th-TH.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/zh-CN.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/zh-TW.json +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/snippets/index.liquid +0 -0
- /package/lib/{app → theme-extension}/template/embed-app/theme-app/snippets/index_css.liquid +0 -0
- /package/lib/{app → theme-extension}/utils/config.js +0 -0
- /package/lib/{app → theme-extension}/utils/index.js +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const { connectionAppRequest } = require('../../api/partnerOpenapi');
|
|
2
|
+
const {
|
|
3
|
+
upsertThemeExtensionRequest,
|
|
4
|
+
createThemeExtensionVersionRequest,
|
|
5
|
+
getThemeExtensionVersionTaskRequest
|
|
6
|
+
} = require('../../api/openapi');
|
|
7
|
+
|
|
8
|
+
async function upsertTheme(extension, appClientId, partnerId, appAccessToken) {
|
|
9
|
+
// 有 extension_id 则 升级,没有则创建
|
|
10
|
+
if (extension.extension_id && extension.extension_version) {
|
|
11
|
+
// 更改 extension 信息
|
|
12
|
+
const { extension_id } = await upsertThemeExtensionRequest(appClientId, partnerId, {
|
|
13
|
+
extension_id: extension.extension_id,
|
|
14
|
+
resource_url: extension.resource_url,
|
|
15
|
+
title: extension.extension_name
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// 升级 version
|
|
19
|
+
const { task_id } = await createThemeExtensionVersionRequest(appClientId, partnerId, {
|
|
20
|
+
extension_id: extension.extension_id,
|
|
21
|
+
version: extension.extension_version,
|
|
22
|
+
resource_url: extension.resource_url
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
// 轮询 version 创建状态
|
|
27
|
+
let timer = null;
|
|
28
|
+
const maxRetry = 10; // 最大重试次数 10s 后超时
|
|
29
|
+
let retry = 0;
|
|
30
|
+
|
|
31
|
+
timer = setInterval(async () => {
|
|
32
|
+
if (retry > maxRetry) {
|
|
33
|
+
clearInterval(timer);
|
|
34
|
+
// 超时,抛出错误
|
|
35
|
+
reject(new Error('create theme extension version timeout'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
retry++;
|
|
39
|
+
|
|
40
|
+
const res = await getThemeExtensionVersionTaskRequest(appClientId, partnerId, task_id);
|
|
41
|
+
if (res.state === 1) {
|
|
42
|
+
clearInterval(timer);
|
|
43
|
+
resolve({
|
|
44
|
+
extension_id,
|
|
45
|
+
extension_version: extension.extension_version,
|
|
46
|
+
extension_version_id: res.version_id
|
|
47
|
+
});
|
|
48
|
+
} else if (res.state === 2) {
|
|
49
|
+
clearInterval(timer);
|
|
50
|
+
reject(new Error(res.message || 'create theme extension version failed'));
|
|
51
|
+
}
|
|
52
|
+
}, 1000);
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
// 创建 extension
|
|
56
|
+
const { extension_id } = await upsertThemeExtensionRequest(appClientId, partnerId, {
|
|
57
|
+
title: extension.extension_name,
|
|
58
|
+
resource_url: extension.resource_url
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 创建 version
|
|
62
|
+
const { task_id } = await createThemeExtensionVersionRequest(appClientId, partnerId, {
|
|
63
|
+
extension_id,
|
|
64
|
+
version: '1.0.0',
|
|
65
|
+
resource_url: extension.resource_url
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const taskResponse = await new Promise((resolve, reject) => {
|
|
69
|
+
// 轮询 version 创建状态
|
|
70
|
+
let timer = null;
|
|
71
|
+
const maxRetry = 10; // 最大重试次数 10s 后超时
|
|
72
|
+
let retry = 0;
|
|
73
|
+
|
|
74
|
+
timer = setInterval(async () => {
|
|
75
|
+
if (retry > maxRetry) {
|
|
76
|
+
clearInterval(timer);
|
|
77
|
+
// 超时,抛出错误
|
|
78
|
+
reject(new Error('create theme extension version timeout'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
retry++;
|
|
82
|
+
|
|
83
|
+
const res = await getThemeExtensionVersionTaskRequest(appClientId, partnerId, task_id);
|
|
84
|
+
if (res.state === 1) {
|
|
85
|
+
clearInterval(timer);
|
|
86
|
+
resolve({
|
|
87
|
+
extension_id,
|
|
88
|
+
extension_version: '1.0.0',
|
|
89
|
+
extension_version_id: res.version_id
|
|
90
|
+
});
|
|
91
|
+
} else if (res.state === 2) {
|
|
92
|
+
clearInterval(timer);
|
|
93
|
+
reject(new Error(res.message || 'create theme extension version failed'));
|
|
94
|
+
}
|
|
95
|
+
}, 1000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 绑定
|
|
99
|
+
const connectionResponse = await connectionAppRequest({
|
|
100
|
+
app_client_id: appClientId,
|
|
101
|
+
extension_id,
|
|
102
|
+
access_token: appAccessToken.access_token
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (connectionResponse.code !== 'Success') {
|
|
106
|
+
throw new Error(connectionResponse.message || 'connection theme extension failed');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return taskResponse;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = upsertTheme;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const FormData = require('form-data');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 计算文件 md5 并转 base64
|
|
8
|
+
*/
|
|
9
|
+
function calcFileMd5Base64(filePath) {
|
|
10
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
11
|
+
const hash = crypto.createHash('md5').update(fileBuffer).digest();
|
|
12
|
+
return hash.toString('base64');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 上传文件到 OSS
|
|
17
|
+
* @param {Object} params
|
|
18
|
+
* @param {string} params.filePath 文件路径
|
|
19
|
+
* @param {string} params.md5 文件 md5(base64 编码)
|
|
20
|
+
* @param {object} params.postPolicy getPostPolicy 的返回值
|
|
21
|
+
* @returns {Promise<void>}
|
|
22
|
+
*/
|
|
23
|
+
async function uploadFileToOSS({ filePath, md5, postPolicy }) {
|
|
24
|
+
const form = new FormData();
|
|
25
|
+
form.append('key', postPolicy.key);
|
|
26
|
+
form.append('policy', postPolicy.policy);
|
|
27
|
+
form.append('x-oss-signature', postPolicy['x-oss-signature']);
|
|
28
|
+
form.append('OSSAccessKeyId', postPolicy.OSSAccessKeyId);
|
|
29
|
+
form.append('x-oss-signature-version', postPolicy['x-oss-signature-version']);
|
|
30
|
+
form.append('x-oss-credential', postPolicy['x-oss-credential']);
|
|
31
|
+
form.append('x-oss-date', postPolicy['x-oss-date']);
|
|
32
|
+
form.append('success_action_status', '201');
|
|
33
|
+
form.append('file', fs.createReadStream(filePath));
|
|
34
|
+
const headers = {
|
|
35
|
+
...form.getHeaders(),
|
|
36
|
+
'x-oss-meta-md5': md5
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
await axios.post(postPolicy.endpoint, form, { headers });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
calcFileMd5Base64,
|
|
44
|
+
uploadFileToOSS
|
|
45
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { getPartnerListRequest, getAppCompleteInfoRequest } = require('../../api/cli');
|
|
2
|
+
const { selectOne } = require('../../utils/views/select');
|
|
3
|
+
|
|
4
|
+
// 选择 partner
|
|
5
|
+
async function promptPartnerSelection() {
|
|
6
|
+
const result = await getPartnerListRequest();
|
|
7
|
+
return await selectOne(result.partners, {
|
|
8
|
+
message: 'Which organization is this work for?',
|
|
9
|
+
autoPickSingle: false,
|
|
10
|
+
formatChoice: (p) => ({ name: p.business_name, value: p })
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 获取 partner
|
|
15
|
+
async function getPartner(clientId) {
|
|
16
|
+
try {
|
|
17
|
+
let partner;
|
|
18
|
+
if (!clientId) {
|
|
19
|
+
partner = await promptPartnerSelection();
|
|
20
|
+
} else {
|
|
21
|
+
const contextInfo = await getAppCompleteInfoRequest(clientId);
|
|
22
|
+
partner = contextInfo.partner;
|
|
23
|
+
}
|
|
24
|
+
return partner;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new Error(`Failed to get partner: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 根据 appList 判断创建或连接
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {*} appListRes 获取 app 列表的请求结果
|
|
34
|
+
* @returns 是否创建
|
|
35
|
+
* @description 根据 app 列表判断创建或连接
|
|
36
|
+
*/
|
|
37
|
+
async function promptCreateOrConnect(appListRes) {
|
|
38
|
+
if (appListRes.total === 0) return true;
|
|
39
|
+
|
|
40
|
+
return await selectOne(
|
|
41
|
+
[
|
|
42
|
+
{ name: 'Yes, create it as a new app', value: true },
|
|
43
|
+
{ name: 'No, connect it to an existing app', value: false }
|
|
44
|
+
],
|
|
45
|
+
{ message: 'Create this project as a new app on Shoplazza?', autoPickSingle: false }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
getPartner,
|
|
51
|
+
promptCreateOrConnect
|
|
52
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const Conf = require('conf');
|
|
2
|
+
|
|
3
|
+
class BaseStore {
|
|
4
|
+
constructor(projectName, configName) {
|
|
5
|
+
this.store = new Conf({ projectName, configName });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get(key, defaultValue = undefined) {
|
|
9
|
+
return this.store.get(key, defaultValue);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
set(keyOrObj, value) {
|
|
13
|
+
if (keyOrObj && typeof keyOrObj === 'object') {
|
|
14
|
+
for (const [k, v] of Object.entries(keyOrObj)) {
|
|
15
|
+
this.store.set(k, v);
|
|
16
|
+
}
|
|
17
|
+
} else if (typeof keyOrObj === 'string') {
|
|
18
|
+
this.store.set(keyOrObj, value);
|
|
19
|
+
} else {
|
|
20
|
+
throw new TypeError('Invalid arguments: expected (string, any) or (object)');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
delete(key) {
|
|
25
|
+
this.store.delete(key);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clear() {
|
|
29
|
+
this.store.clear();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
has(key) {
|
|
33
|
+
return this.store.has(key);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = BaseStore;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const BaseStore = require('./base-store');
|
|
2
|
+
const { STORE_CONFIG } = require('./config');
|
|
3
|
+
|
|
4
|
+
class ConfigStore extends BaseStore {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(STORE_CONFIG.PROJECT_NAME, STORE_CONFIG.STORE_NAMES.APP_CONFIG);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// 获取项目配置
|
|
10
|
+
getProjectConfig(projectPath) {
|
|
11
|
+
return this.get(projectPath);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @description 设置项目配置
|
|
16
|
+
* @param {string} projectPath 项目路径
|
|
17
|
+
* @param {{ clientId: string, directory: string, configFile: string }} config 项目配置
|
|
18
|
+
*/
|
|
19
|
+
setProjectConfig(projectPath, config) {
|
|
20
|
+
this.set(projectPath, config);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @description 删除项目配置
|
|
25
|
+
* @param {string} projectPath 项目路径
|
|
26
|
+
*/
|
|
27
|
+
removeProjectConfig(projectPath) {
|
|
28
|
+
this.delete(projectPath);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @description 获取所有项目配置
|
|
33
|
+
* @returns {Object} 所有项目配置
|
|
34
|
+
*/
|
|
35
|
+
getAllProjects() {
|
|
36
|
+
return this.store.store;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description 根据 clientId 查找项目
|
|
41
|
+
* @param {string} clientId 应用 clientId
|
|
42
|
+
* @returns {Object} 项目配置
|
|
43
|
+
*/
|
|
44
|
+
findProjectByClientId(clientId) {
|
|
45
|
+
const projects = this.getAllProjects();
|
|
46
|
+
for (const [path, config] of Object.entries(projects)) {
|
|
47
|
+
if (config.clientId === clientId) {
|
|
48
|
+
return { path, config };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = ConfigStore;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// 统一配置
|
|
2
|
+
const STORE_CONFIG = {
|
|
3
|
+
PROJECT_NAME: 'shoplazza-cli-app',
|
|
4
|
+
STORE_NAMES: {
|
|
5
|
+
SSO: 'sso',
|
|
6
|
+
APP_CONFIG: 'config',
|
|
7
|
+
APP_INSTALL: 'app-install'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const SSO_STORE_KEY = {
|
|
12
|
+
ACCESS_TOKEN: 'access_token',
|
|
13
|
+
EXPIRES_IN: 'expires_in',
|
|
14
|
+
SESSION_ID: 'session_id',
|
|
15
|
+
TOKEN_TYPE: 'token_type'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
STORE_CONFIG,
|
|
20
|
+
SSO_STORE_KEY
|
|
21
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const SSOStore = require('./sso-store');
|
|
2
|
+
const ConfigStore = require('./config-store');
|
|
3
|
+
const InstallStore = require('./install-store');
|
|
4
|
+
|
|
5
|
+
// 创建实例
|
|
6
|
+
const ssoStore = new SSOStore();
|
|
7
|
+
const configStore = new ConfigStore();
|
|
8
|
+
const installStore = new InstallStore();
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
ssoStore,
|
|
12
|
+
configStore,
|
|
13
|
+
installStore,
|
|
14
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const BaseStore = require('./base-store');
|
|
2
|
+
const { STORE_CONFIG } = require('./config');
|
|
3
|
+
|
|
4
|
+
class InstallStore extends BaseStore {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(STORE_CONFIG.PROJECT_NAME, STORE_CONFIG.STORE_NAMES.APP_INSTALL);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// 获取应用安装信息
|
|
10
|
+
getInstallInfo(clientId) {
|
|
11
|
+
return this.get(clientId);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 设置应用安装信息
|
|
15
|
+
setInstallInfo(clientId, installData) {
|
|
16
|
+
this.set(clientId, installData);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 删除应用安装信息
|
|
20
|
+
removeInstallInfo(clientId) {
|
|
21
|
+
this.delete(clientId);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 获取所有安装信息
|
|
25
|
+
getAllInstalls() {
|
|
26
|
+
return this.store.store;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 根据店铺域名查找安装信息
|
|
30
|
+
findInstallByStoreDomain(storeDomain) {
|
|
31
|
+
const installs = this.getAllInstalls();
|
|
32
|
+
for (const [clientId, installData] of Object.entries(installs)) {
|
|
33
|
+
if (installData.store_domain === storeDomain) {
|
|
34
|
+
return { clientId, installData };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = InstallStore;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const BaseStore = require('./base-store');
|
|
2
|
+
const { STORE_CONFIG, SSO_STORE_KEY } = require('./config');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} SSOInfo
|
|
6
|
+
* @property {string} access_token - 认证 token
|
|
7
|
+
* @property {number} expires_in - 过期时间(秒)
|
|
8
|
+
* @property {string} session_id - 会话 ID
|
|
9
|
+
* @property {string} token_type - token 类型
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
class SSOStore extends BaseStore {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(STORE_CONFIG.PROJECT_NAME, STORE_CONFIG.STORE_NAMES.SSO);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @description 获取 SSO 认证信息
|
|
19
|
+
* @returns {SSOInfo} SSO 认证信息
|
|
20
|
+
*/
|
|
21
|
+
getSSOInfo() {
|
|
22
|
+
return {
|
|
23
|
+
access_token: this.get(SSO_STORE_KEY.ACCESS_TOKEN),
|
|
24
|
+
expires_in: this.get(SSO_STORE_KEY.EXPIRES_IN),
|
|
25
|
+
session_id: this.get(SSO_STORE_KEY.SESSION_ID),
|
|
26
|
+
token_type: this.get(SSO_STORE_KEY.TOKEN_TYPE)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @description 设置 SSO 认证信息
|
|
32
|
+
* @param {SSOInfo} ssoData SSO 认证信息
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
35
|
+
setSSOInfo(ssoData) {
|
|
36
|
+
this.set(ssoData);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description 获取 SSO 访问令牌
|
|
41
|
+
* @returns {string} SSO 访问令牌 access_token
|
|
42
|
+
*/
|
|
43
|
+
getAccessToken() {
|
|
44
|
+
return this.get(SSO_STORE_KEY.ACCESS_TOKEN);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @description 清除 SSO 认证信息
|
|
49
|
+
*/
|
|
50
|
+
clearSSO() {
|
|
51
|
+
this.clear();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = SSOStore;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 控制异步任务最大并发数
|
|
3
|
+
* @param {number} limit 最大并发数
|
|
4
|
+
* @param {Array} tasks 任务数组(每个任务是返回 Promise 的函数)
|
|
5
|
+
* @returns {Promise<Array>} 按顺序返回所有任务的结果
|
|
6
|
+
*/
|
|
7
|
+
async function asyncPool(limit, tasks) {
|
|
8
|
+
const results = [];
|
|
9
|
+
let running = 0; // 当前运行中的任务数
|
|
10
|
+
let index = 0; // 下一个要执行的任务索引
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
function runNext() {
|
|
14
|
+
// 全部执行完毕
|
|
15
|
+
if (index === tasks.length && running === 0) {
|
|
16
|
+
return resolve(results);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 并发已满,等待
|
|
20
|
+
while (running < limit && index < tasks.length) {
|
|
21
|
+
const currentIndex = index++;
|
|
22
|
+
const task = tasks[currentIndex];
|
|
23
|
+
|
|
24
|
+
running++;
|
|
25
|
+
Promise.resolve()
|
|
26
|
+
.then(task)
|
|
27
|
+
.then((res) => {
|
|
28
|
+
results[currentIndex] = res;
|
|
29
|
+
})
|
|
30
|
+
.catch((err) => reject(err))
|
|
31
|
+
.finally(() => {
|
|
32
|
+
running--;
|
|
33
|
+
runNext(); // 任务结束后补位
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
runNext();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = asyncPool;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function isDebug() {
|
|
2
|
+
return process.argv.indexOf('--debug') >= 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function isDevDebug() {
|
|
6
|
+
return process.argv.join('').indexOf('--debugdev') >= 0;
|
|
7
|
+
}
|
|
8
|
+
function isStgDebug() {
|
|
9
|
+
return process.argv.join('').indexOf('--debugstg') >= 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
isDebug,
|
|
14
|
+
isDevDebug,
|
|
15
|
+
isStgDebug
|
|
16
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const dotenv = require('dotenv');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
async function writeEnvWithDotenv(filePath, kv = {}) {
|
|
6
|
+
let src = '';
|
|
7
|
+
try {
|
|
8
|
+
src = await fs.readFile(filePath, 'utf8');
|
|
9
|
+
} catch (e) {
|
|
10
|
+
if (e.code !== 'ENOENT') throw e; // 其他错误抛出;不存在则视为空
|
|
11
|
+
}
|
|
12
|
+
const parsed = src ? dotenv.parse(src) : {};
|
|
13
|
+
|
|
14
|
+
// 合并新值
|
|
15
|
+
Object.assign(parsed, kv);
|
|
16
|
+
|
|
17
|
+
// 手动序列化(注释与原格式将丢失)
|
|
18
|
+
const lines = Object.entries(parsed).map(([k, v]) => `${k}=${v ?? ''}`);
|
|
19
|
+
await fs.writeFile(filePath, lines.join(os.EOL) + os.EOL, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
writeEnvWithDotenv
|
|
24
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { errorMessageRender } = require('./views/message');
|
|
2
|
+
|
|
3
|
+
// 包装命令函数,添加错误处理
|
|
4
|
+
const withErrorHandling = (commandFn) => {
|
|
5
|
+
return async (...args) => {
|
|
6
|
+
try {
|
|
7
|
+
return await commandFn(...args);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (process.env.DEBUG) {
|
|
10
|
+
console.error(error);
|
|
11
|
+
}
|
|
12
|
+
errorMessageRender(error.message || error);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
withErrorHandling
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
|
|
4
|
+
// 检查是否安装 git
|
|
5
|
+
function checkGit() {
|
|
6
|
+
try {
|
|
7
|
+
execSync('git --version');
|
|
8
|
+
} catch (error) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
[
|
|
11
|
+
'Git is not installed. Please install Git to continue:',
|
|
12
|
+
chalk.underline('https://git-scm.com/book/en/v2/Getting-Started-Installing-Git')
|
|
13
|
+
].join('\n')
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
checkGit
|
|
20
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 读取并解析JSON文件
|
|
5
|
+
* @param {string} filePath - JSON文件路径
|
|
6
|
+
* @returns {Object} - 解析后的对象
|
|
7
|
+
*/
|
|
8
|
+
function parseJsonFile(filePath) {
|
|
9
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
10
|
+
return JSON.parse(content);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 修改package.json文件
|
|
15
|
+
* @param {string} filePath - package.json文件路径
|
|
16
|
+
* @param {function(Object):Object} callback - 修改函数
|
|
17
|
+
*/
|
|
18
|
+
async function modifyJsonFile(filePath, callback) {
|
|
19
|
+
const packageJson = parseJsonFile(filePath);
|
|
20
|
+
const result = await callback(packageJson);
|
|
21
|
+
fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
parseJsonFile,
|
|
26
|
+
modifyJsonFile
|
|
27
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
/**
|
|
3
|
+
* 将文件名转换为连字符格式(kebab-case)
|
|
4
|
+
* @param {string} str - 需要处理的字符串
|
|
5
|
+
* @returns {string} - 处理后的字符串
|
|
6
|
+
*/
|
|
7
|
+
function slugify(str) {
|
|
8
|
+
return str
|
|
9
|
+
.toLowerCase() // 转为小写
|
|
10
|
+
.trim() // 去除前后空格
|
|
11
|
+
.replace(/[^\w\s-]/g, '') // 移除所有非单词字符、空格和连字符
|
|
12
|
+
.replace(/[\s_-]+/g, '-') // 将空格和下划线替换为连字符
|
|
13
|
+
.replace(/^-+|-+$/g, ''); // 移除开头和结尾的连字符
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 判断路径是否存在
|
|
18
|
+
* @param {string} path - 需要判断的路径
|
|
19
|
+
* @returns {boolean} - 路径是否存在
|
|
20
|
+
*/
|
|
21
|
+
function existsPath(path) {
|
|
22
|
+
try {
|
|
23
|
+
fs.accessSync(path, fs.constants.F_OK);
|
|
24
|
+
return true; // 路径存在
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return false; // 路径不存在
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
slugify,
|
|
32
|
+
existsPath
|
|
33
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function getArchPlatform() {
|
|
2
|
+
let platform;
|
|
3
|
+
let arch;
|
|
4
|
+
switch (process.platform.toLowerCase()) {
|
|
5
|
+
case 'darwin':
|
|
6
|
+
platform = 'macos';
|
|
7
|
+
break;
|
|
8
|
+
case 'linux':
|
|
9
|
+
platform = 'linux';
|
|
10
|
+
break;
|
|
11
|
+
case 'win32':
|
|
12
|
+
platform = 'windows';
|
|
13
|
+
break;
|
|
14
|
+
default:
|
|
15
|
+
throw Error(`Unsupported platform ${processPlatform}`);
|
|
16
|
+
}
|
|
17
|
+
switch (process.arch.toLowerCase()) {
|
|
18
|
+
case 'arm':
|
|
19
|
+
case 'arm64':
|
|
20
|
+
arch = 'arm';
|
|
21
|
+
break;
|
|
22
|
+
// A 32 bit arch likely needs that someone has 32bit Node installed on a
|
|
23
|
+
// 64 bit system, and wasmtime doesn't support 32bit anyway.
|
|
24
|
+
case 'ia32':
|
|
25
|
+
case 'x64':
|
|
26
|
+
arch = 'x86_64';
|
|
27
|
+
break;
|
|
28
|
+
default:
|
|
29
|
+
throw Error(`Unsupported architecture ${processArch}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return `${arch}-${platform}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
getArchPlatform
|
|
37
|
+
};
|