eprolo-cli 1.0.43 → 1.0.45
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/bin/eprolo-cli.js +84 -58
- package/package.json +1 -1
package/bin/eprolo-cli.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
7
|
-
const
|
|
7
|
+
const https = require("https");
|
|
8
8
|
|
|
9
9
|
const LOGIN_API = "https://wixtest.eprolo.com/v1/auth/verify-code";
|
|
10
|
-
const
|
|
10
|
+
const REFRESH_API = "https://wixtest.eprolo.com/v1/auth/refresh-code";
|
|
11
|
+
const CONFIG_DIR = path.join(os.homedir(), ".eprolo-toolkit");
|
|
11
12
|
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
12
13
|
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
|
-
// 凭据文件读写(~/.
|
|
15
|
+
// 凭据文件读写(~/.eprolo-toolkit/config.json)
|
|
15
16
|
// 框架判定连接成功的唯一依据:凭据文件出现
|
|
16
17
|
// 框架读顶层明文 account 字段做授权卡片标签
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -32,26 +33,6 @@ function writeConfig(config) {
|
|
|
32
33
|
// 参数解析
|
|
33
34
|
// ---------------------------------------------------------------------------
|
|
34
35
|
|
|
35
|
-
function parseArgs(argv) {
|
|
36
|
-
const args = { _: [] };
|
|
37
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
38
|
-
const token = argv[i];
|
|
39
|
-
if (!token.startsWith("--")) {
|
|
40
|
-
args._.push(token);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
const key = token.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
44
|
-
const next = argv[i + 1];
|
|
45
|
-
if (!next || next.startsWith("--")) {
|
|
46
|
-
args[key] = true;
|
|
47
|
-
} else {
|
|
48
|
-
args[key] = next;
|
|
49
|
-
i += 1;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return args;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
36
|
function readFlag(argv, flag) {
|
|
56
37
|
const idx = argv.indexOf(flag);
|
|
57
38
|
if (idx === -1 || idx + 1 >= argv.length) return undefined;
|
|
@@ -59,21 +40,22 @@ function readFlag(argv, flag) {
|
|
|
59
40
|
}
|
|
60
41
|
|
|
61
42
|
// ---------------------------------------------------------------------------
|
|
62
|
-
//
|
|
43
|
+
// HTTPS POST 请求
|
|
63
44
|
// ---------------------------------------------------------------------------
|
|
64
45
|
|
|
65
|
-
function postJson(url, body) {
|
|
46
|
+
function postJson(url, body, headers = {}) {
|
|
66
47
|
return new Promise((resolve, reject) => {
|
|
67
48
|
const target = new URL(url);
|
|
68
49
|
const payload = JSON.stringify(body);
|
|
69
|
-
const req =
|
|
50
|
+
const req = https.request({
|
|
70
51
|
hostname: target.hostname,
|
|
71
|
-
port: target.port,
|
|
52
|
+
port: target.port || 443,
|
|
72
53
|
path: `${target.pathname}${target.search}`,
|
|
73
54
|
method: "POST",
|
|
74
55
|
headers: {
|
|
75
56
|
"content-type": "application/json",
|
|
76
|
-
"content-length": Buffer.byteLength(payload)
|
|
57
|
+
"content-length": Buffer.byteLength(payload),
|
|
58
|
+
...headers
|
|
77
59
|
}
|
|
78
60
|
}, (res) => {
|
|
79
61
|
const chunks = [];
|
|
@@ -86,13 +68,7 @@ function postJson(url, body) {
|
|
|
86
68
|
} catch (_) {
|
|
87
69
|
data = { raw: text };
|
|
88
70
|
}
|
|
89
|
-
|
|
90
|
-
const err = new Error(`HTTP ${res.statusCode} ${res.statusMessage || ""}`.trim());
|
|
91
|
-
err.response = data;
|
|
92
|
-
reject(err);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
resolve(data);
|
|
71
|
+
resolve({ statusCode: res.statusCode, data });
|
|
96
72
|
});
|
|
97
73
|
});
|
|
98
74
|
req.on("error", reject);
|
|
@@ -101,32 +77,79 @@ function postJson(url, body) {
|
|
|
101
77
|
});
|
|
102
78
|
}
|
|
103
79
|
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// 带 401 自动刷新 token 重试的请求
|
|
82
|
+
// 1. 发起请求
|
|
83
|
+
// 2. 如果返回 401 → 调用 refresh-code 刷新 token → 回写凭据文件 → 重试一次
|
|
84
|
+
// 3. 仍然 401 → 抛错
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
async function postJsonWithRetry(url, body, config) {
|
|
88
|
+
const headers = {};
|
|
89
|
+
if (config.accessToken) headers["authorization"] = `Bearer ${config.accessToken}`;
|
|
90
|
+
|
|
91
|
+
let result = await postJson(url, body, headers);
|
|
92
|
+
|
|
93
|
+
if (result.statusCode === 401) {
|
|
94
|
+
// 刷新 token
|
|
95
|
+
const refreshRes = await postJson(REFRESH_API, {
|
|
96
|
+
accessToken: config.accessToken,
|
|
97
|
+
accountId: config.accountId,
|
|
98
|
+
shopId: config.shopId
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (refreshRes.statusCode >= 200 && refreshRes.statusCode < 300 && refreshRes.data.accessToken) {
|
|
102
|
+
// 更新凭据文件
|
|
103
|
+
config.accessToken = refreshRes.data.accessToken;
|
|
104
|
+
if (refreshRes.data.account) config.account = refreshRes.data.account;
|
|
105
|
+
writeConfig(config);
|
|
106
|
+
|
|
107
|
+
// 用新 token 重试
|
|
108
|
+
headers["authorization"] = `Bearer ${config.accessToken}`;
|
|
109
|
+
result = await postJson(url, body, headers);
|
|
110
|
+
} else {
|
|
111
|
+
const err = new Error("Token 刷新失败,请重新登录授权");
|
|
112
|
+
err.response = refreshRes.data;
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
118
|
+
const err = new Error(`HTTP ${result.statusCode}`);
|
|
119
|
+
err.response = result.data;
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result.data;
|
|
124
|
+
}
|
|
125
|
+
|
|
104
126
|
// ---------------------------------------------------------------------------
|
|
105
127
|
// D2 · login 命令
|
|
106
|
-
// 输入契约(框架 → CLI):
|
|
107
|
-
// userCode ← env 通道 process.env.DXB_USER_CODE
|
|
108
|
-
// defaultShop ← arg 通道 --default-shop
|
|
109
|
-
// 行为契约:
|
|
110
|
-
// 成功 → 写凭据文件 + exit 0
|
|
111
|
-
// 失败 → 非零退出 + stderr 错误信息
|
|
112
|
-
// ❌ 禁止交互式 prompt
|
|
113
128
|
// ---------------------------------------------------------------------------
|
|
114
129
|
|
|
115
130
|
async function login(argv) {
|
|
116
131
|
const userCode = process.env.EPROLO_USER_CODE; // env 通道
|
|
117
132
|
if (!userCode) { console.error("缺少用户密文:请在授权弹窗中填写"); process.exit(2); }
|
|
118
133
|
|
|
119
|
-
const defaultShop = readFlag(argv, "--default-shop");
|
|
134
|
+
const defaultShop = readFlag(argv, "--default-shop"); // arg 通道,可能不存在
|
|
120
135
|
|
|
121
|
-
const res = await
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
const res = await postJson(LOGIN_API, { openApiKey: userCode });
|
|
137
|
+
|
|
138
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
139
|
+
console.error(JSON.stringify({
|
|
140
|
+
success: false,
|
|
141
|
+
errorMessage: "密文验证失败,请到账号设置页重新复制",
|
|
142
|
+
statusCode: res.statusCode,
|
|
143
|
+
response: res.data
|
|
144
|
+
}));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const { account, accessToken, accountId, shopId } = res.data;
|
|
149
|
+
const config = { account, accessToken, defaultShop, accountId, shopId, version: 1 };
|
|
150
|
+
writeConfig(config);
|
|
126
151
|
|
|
127
|
-
|
|
128
|
-
writeConfig({ account, accessToken, defaultShop,accountId,shopId }); // → D3
|
|
129
|
-
console.log("login ok"); // 不打印任何敏感值
|
|
152
|
+
console.log("login ok");
|
|
130
153
|
process.exit(0);
|
|
131
154
|
}
|
|
132
155
|
|
|
@@ -160,28 +183,31 @@ function authLogout() {
|
|
|
160
183
|
// ---------------------------------------------------------------------------
|
|
161
184
|
|
|
162
185
|
function printHelp() {
|
|
163
|
-
console.log(`
|
|
186
|
+
console.log(`EPROLO CLI (cli-login 形态)
|
|
164
187
|
|
|
165
188
|
Usage:
|
|
166
|
-
eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入
|
|
189
|
+
eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入 EPROLO_USER_CODE)
|
|
167
190
|
eprolo-cli auth status 查看授权状态
|
|
168
191
|
eprolo-cli auth logout 退出授权
|
|
169
192
|
|
|
170
193
|
Authorization:
|
|
171
194
|
框架通过 fieldInjection 把表单值递交给 CLI:
|
|
172
|
-
userCode → env 通道 → process.env.
|
|
195
|
+
userCode → env 通道 → process.env.EPROLO_USER_CODE
|
|
173
196
|
defaultShop → arg 通道 → --default-shop
|
|
174
197
|
|
|
175
|
-
连接成功的唯一判定:凭据文件 ~/.
|
|
198
|
+
连接成功的唯一判定:凭据文件 ~/.eprolo-toolkit/config.json 出现
|
|
176
199
|
凭据文件顶层明文 account 字段用于授权卡片标签
|
|
177
200
|
|
|
178
201
|
登录时调用 ${LOGIN_API} 验证密文
|
|
202
|
+
401 时自动调用 ${REFRESH_API} 刷新 token 并重试
|
|
179
203
|
|
|
180
|
-
Credential file (~/.
|
|
204
|
+
Credential file (~/.eprolo-toolkit/config.json):
|
|
181
205
|
{
|
|
182
|
-
"account": "user@example.com",
|
|
183
|
-
"
|
|
206
|
+
"account": "user@example.com",
|
|
207
|
+
"accessToken": "...",
|
|
184
208
|
"defaultShop": "shop-001",
|
|
209
|
+
"accountId": "...",
|
|
210
|
+
"shopId": "...",
|
|
185
211
|
"version": 1
|
|
186
212
|
}
|
|
187
213
|
`);
|
package/package.json
CHANGED