cyymall-cli 0.1.6 → 0.1.8
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 +19 -4
- package/package.json +1 -1
- package/src/Untitled +1 -0
- package/src/cli.js +43 -4
- package/src/commands/apiCall.js +1 -1
- package/src/commands/auth.js +83 -5
- package/src/commands/cart.js +1 -5
- package/src/commands/order.js +6 -30
- package/src/commands/product.js +1 -5
- package/src/commands/shop.js +4 -20
- package/src/config.js +67 -1
- package/src/http.js +1 -2
package/README.md
CHANGED
|
@@ -57,6 +57,7 @@ npm publish --otp=123456
|
|
|
57
57
|
cyy config path
|
|
58
58
|
cyy auth send-code --phone <mobile>
|
|
59
59
|
cyy auth login --phone <mobile> --code <sms>
|
|
60
|
+
cyy auth import --token <appToken> [--member-id <id>] [--shop-id <id>] [--site-id <id>]
|
|
60
61
|
cyy auth whoami
|
|
61
62
|
cyy shop list
|
|
62
63
|
cyy shop sites --shop-id <门店ID>
|
|
@@ -70,14 +71,28 @@ cyy serve --port 8787
|
|
|
70
71
|
|
|
71
72
|
## Session vs bootstrap env
|
|
72
73
|
|
|
73
|
-
**After `cyy auth login`
|
|
74
|
-
后续 **`api call`、`product search`、`order quick`** 等命令都会从该文件加载并组装 Header,正常情况下不再需要设置 `CYY_BOOTSTRAP_*`。
|
|
74
|
+
**After `cyy auth login` or `cyy auth import` succeeds**, session fields live under **`~/.cyymall/config.json`** (`token`, `member_id`, `shop_id`, `site_id`, `version_code`, …). **`api call`、`product search`、`order quick`** 等命令读取**有效会话**:先合并配置文件,再用下表 `CYY_*` 覆盖(适合 Gateway / MCP 子进程注入、不落盘)。
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
**External token (skip SMS login):**
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cyy auth import --token "<appToken>" --member-id "<id>" --shop-id "<id>"
|
|
80
|
+
# or ephemeral for one process:
|
|
81
|
+
export CYY_TOKEN="<appToken>"
|
|
82
|
+
export CYY_SHOP_ID="<id>"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**`CYY_BOOTSTRAP_*` 仅用于「尚未登录」时**(无 saved token 且无 `CYY_TOKEN`)。已登录时业务请求优先使用配置文件 + `CYY_TOKEN` 等会话变量。
|
|
77
86
|
|
|
78
87
|
| Variable | Purpose |
|
|
79
88
|
|----------|---------|
|
|
80
89
|
| `CYY_BASE_URL` | Override API host (default `https://dhcmall.ifoodbuy.com`) |
|
|
90
|
+
| `CYY_TOKEN` | Session token for current process (overrides saved config) |
|
|
91
|
+
| `CYY_MEMBER_ID` | Override `member_id` header |
|
|
92
|
+
| `CYY_SHOP_ID` | Override `shop_id` header |
|
|
93
|
+
| `CYY_SITE_ID` | Override `site_id` header |
|
|
94
|
+
| `CYY_VERSION_CODE` | Override `version_code` header |
|
|
95
|
+
| `CYY_PHONE` | Optional label in config display |
|
|
81
96
|
| `CYY_ENCRYPT_OFF` | `1` / `true`: disable hybrid crypto (plaintext `sendCodeV2`) |
|
|
82
97
|
| `CYY_ENCRYPT_DEBUG` | `1` / `true`: print RSA/AES diagnostics for hybrid decrypt failures |
|
|
83
98
|
| `CYY_BOOTSTRAP_TOKEN` | Optional - **only before login**, gateway may expect placeholder token |
|
|
@@ -92,4 +107,4 @@ cyy serve --port 8787
|
|
|
92
107
|
|
|
93
108
|
## Implementation phases
|
|
94
109
|
|
|
95
|
-
See [`../README_CLI.md`](../README_CLI.md) for staged rollout and acceptance notes.
|
|
110
|
+
See [`../README_CLI.md`](../README_CLI.md) for staged rollout and acceptance notes.
|
package/package.json
CHANGED
package/src/Untitled
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
shop
|
package/src/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ const path = require("path");
|
|
|
4
4
|
const pkg = require(path.join(__dirname, "..", "package.json"));
|
|
5
5
|
|
|
6
6
|
const config = require("./config");
|
|
7
|
+
const http = require("./http");
|
|
7
8
|
const apiCall = require("./commands/apiCall");
|
|
8
9
|
const auth = require("./commands/auth");
|
|
9
10
|
const product = require("./commands/product");
|
|
@@ -31,14 +32,26 @@ cfgCmd
|
|
|
31
32
|
|
|
32
33
|
cfgCmd
|
|
33
34
|
.command("show")
|
|
34
|
-
.description("Show config (token masked)")
|
|
35
|
+
.description("Show config (token masked); includes effective session and env overrides")
|
|
35
36
|
.action(() => {
|
|
36
|
-
const
|
|
37
|
-
|
|
37
|
+
const file = config.loadConfig();
|
|
38
|
+
const session = config.getSession();
|
|
39
|
+
const envOverrides = config.getEnvSessionOverrideKeys();
|
|
40
|
+
if (!config.hasAuthToken(session) && !file) {
|
|
38
41
|
console.log(JSON.stringify({ loggedIn: false }, null, 2));
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
|
-
const masked = {
|
|
44
|
+
const masked = {
|
|
45
|
+
loggedIn: config.hasAuthToken(session),
|
|
46
|
+
saved: file
|
|
47
|
+
? { ...file, token: config.maskToken(String(file.token || "")) }
|
|
48
|
+
: null,
|
|
49
|
+
effective: {
|
|
50
|
+
...session,
|
|
51
|
+
token: config.maskToken(String(session.token || "")),
|
|
52
|
+
},
|
|
53
|
+
envOverrides: envOverrides.length ? envOverrides : undefined,
|
|
54
|
+
};
|
|
42
55
|
console.log(JSON.stringify(masked, null, 2));
|
|
43
56
|
});
|
|
44
57
|
|
|
@@ -92,6 +105,32 @@ authCmd
|
|
|
92
105
|
await auth.whoami();
|
|
93
106
|
});
|
|
94
107
|
|
|
108
|
+
authCmd
|
|
109
|
+
.command("import")
|
|
110
|
+
.description(
|
|
111
|
+
"Import external session token into ~/.cyymall/config.json (skip SMS login); optional --no-verify",
|
|
112
|
+
)
|
|
113
|
+
.option("--token <t>", "App session token (appToken / Authorization value)")
|
|
114
|
+
.option("--token-file <file>", "Read token from UTF-8 file (e.g. secret mount)")
|
|
115
|
+
.option("--member-id <id>", "memberId header")
|
|
116
|
+
.option("--shop-id <id>", "shopId header")
|
|
117
|
+
.option("--site-id <id>", "siteId header (default 1 if omitted and not in saved config)")
|
|
118
|
+
.option("--version-code <n>", "version-code header")
|
|
119
|
+
.option("--phone <p>", "Optional phone label for config display")
|
|
120
|
+
.option("--no-verify", "Save without calling GET /member/getInfo/V2")
|
|
121
|
+
.action(async (opts) => {
|
|
122
|
+
await auth.importSession({
|
|
123
|
+
token: opts.token,
|
|
124
|
+
tokenFile: opts.tokenFile,
|
|
125
|
+
memberId: opts.memberId,
|
|
126
|
+
shopId: opts.shopId,
|
|
127
|
+
siteId: opts.siteId,
|
|
128
|
+
versionCode: opts.versionCode,
|
|
129
|
+
phone: opts.phone,
|
|
130
|
+
noVerify: Boolean(opts.noVerify),
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
95
134
|
const prod = program.command("product").description("Product helpers");
|
|
96
135
|
|
|
97
136
|
prod
|
package/src/commands/apiCall.js
CHANGED
package/src/commands/auth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const fs = require("fs");
|
|
3
4
|
const crypto = require("crypto");
|
|
4
5
|
const readline = require("readline/promises");
|
|
5
6
|
const { stdin: input, stdout: output } = require("process");
|
|
@@ -258,12 +259,89 @@ async function login(phone, code) {
|
|
|
258
259
|
await loginWithCode(phone, smsCode);
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Persist an externally supplied session (Native App / Gateway token), skipping SMS login.
|
|
264
|
+
* @param {object} opts
|
|
265
|
+
* @param {string} [opts.token]
|
|
266
|
+
* @param {string} [opts.tokenFile]
|
|
267
|
+
* @param {string} [opts.memberId]
|
|
268
|
+
* @param {string} [opts.shopId]
|
|
269
|
+
* @param {string} [opts.siteId]
|
|
270
|
+
* @param {string} [opts.versionCode]
|
|
271
|
+
* @param {string} [opts.phone]
|
|
272
|
+
* @param {boolean} [opts.noVerify]
|
|
273
|
+
*/
|
|
274
|
+
async function importSession(opts) {
|
|
275
|
+
let token = opts.token != null ? String(opts.token).trim() : "";
|
|
276
|
+
if (!token && opts.tokenFile) {
|
|
277
|
+
token = fs.readFileSync(opts.tokenFile, "utf8").trim();
|
|
278
|
+
}
|
|
279
|
+
if (!token) {
|
|
280
|
+
console.error("cyy: --token or --token-file is required");
|
|
265
281
|
process.exit(1);
|
|
266
282
|
}
|
|
283
|
+
|
|
284
|
+
const prev = config.loadConfig() || {};
|
|
285
|
+
const saved = {
|
|
286
|
+
...prev,
|
|
287
|
+
token,
|
|
288
|
+
member_id:
|
|
289
|
+
opts.memberId != null && String(opts.memberId).trim() !== ""
|
|
290
|
+
? String(opts.memberId).trim()
|
|
291
|
+
: String(prev.member_id || ""),
|
|
292
|
+
shop_id:
|
|
293
|
+
opts.shopId != null && String(opts.shopId).trim() !== ""
|
|
294
|
+
? String(opts.shopId).trim()
|
|
295
|
+
: String(prev.shop_id || ""),
|
|
296
|
+
site_id:
|
|
297
|
+
opts.siteId != null && String(opts.siteId).trim() !== ""
|
|
298
|
+
? String(opts.siteId).trim()
|
|
299
|
+
: String(prev.site_id || "1"),
|
|
300
|
+
version_code: String(
|
|
301
|
+
opts.versionCode != null && String(opts.versionCode).trim() !== ""
|
|
302
|
+
? opts.versionCode
|
|
303
|
+
: prev.version_code ?? http.DEFAULT_VERSION,
|
|
304
|
+
),
|
|
305
|
+
phone:
|
|
306
|
+
opts.phone != null && String(opts.phone).trim() !== ""
|
|
307
|
+
? String(opts.phone).trim()
|
|
308
|
+
: String(prev.phone || ""),
|
|
309
|
+
login_time: Math.floor(Date.now() / 1000),
|
|
310
|
+
session_source: "import",
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
config.saveConfig(saved);
|
|
314
|
+
|
|
315
|
+
if (opts.noVerify) {
|
|
316
|
+
const traceId = buildTraceId();
|
|
317
|
+
console.log(
|
|
318
|
+
JSON.stringify(
|
|
319
|
+
{
|
|
320
|
+
success: true,
|
|
321
|
+
code: "OK",
|
|
322
|
+
message: "session imported (not verified)",
|
|
323
|
+
data: {
|
|
324
|
+
phone: saved.phone,
|
|
325
|
+
member_id: saved.member_id,
|
|
326
|
+
shop_id: saved.shop_id,
|
|
327
|
+
site_id: saved.site_id,
|
|
328
|
+
version_code: saved.version_code,
|
|
329
|
+
token_preview: config.maskToken(saved.token),
|
|
330
|
+
},
|
|
331
|
+
traceId,
|
|
332
|
+
},
|
|
333
|
+
null,
|
|
334
|
+
2,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await whoami();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function whoami() {
|
|
344
|
+
const cfg = config.requireAuthSession();
|
|
267
345
|
const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
268
346
|
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
269
347
|
delete headers["Content-Type"];
|
|
@@ -291,4 +369,4 @@ async function whoami() {
|
|
|
291
369
|
process.exit(success ? 0 : 2);
|
|
292
370
|
}
|
|
293
371
|
|
|
294
|
-
module.exports = { login, loginWithCode, sendCode, whoami };
|
|
372
|
+
module.exports = { login, loginWithCode, sendCode, whoami, importSession };
|
package/src/commands/cart.js
CHANGED
|
@@ -10,11 +10,7 @@ const biz = require("../biz");
|
|
|
10
10
|
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
11
11
|
*/
|
|
12
12
|
async function add(opts) {
|
|
13
|
-
const cfg = config.
|
|
14
|
-
if (!cfg?.token) {
|
|
15
|
-
console.error("cyy: not logged in.");
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
13
|
+
const cfg = config.requireAuthSession();
|
|
18
14
|
|
|
19
15
|
let raw = opts.bodyJson;
|
|
20
16
|
if (opts.bodyFile) {
|
package/src/commands/order.js
CHANGED
|
@@ -28,11 +28,7 @@ function envelope(success, message, data, exitCode) {
|
|
|
28
28
|
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
29
29
|
*/
|
|
30
30
|
async function preSettle(opts) {
|
|
31
|
-
const cfg = config.
|
|
32
|
-
if (!cfg?.token) {
|
|
33
|
-
console.error("cyy: not logged in.");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
31
|
+
const cfg = config.requireAuthSession();
|
|
36
32
|
let raw = opts.bodyJson;
|
|
37
33
|
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
38
34
|
if (!raw) {
|
|
@@ -54,11 +50,7 @@ async function preSettle(opts) {
|
|
|
54
50
|
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
55
51
|
*/
|
|
56
52
|
async function confirm(opts) {
|
|
57
|
-
const cfg = config.
|
|
58
|
-
if (!cfg?.token) {
|
|
59
|
-
console.error("cyy: not logged in.");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
53
|
+
const cfg = config.requireAuthSession();
|
|
62
54
|
let raw = opts.bodyJson;
|
|
63
55
|
if (opts.bodyFile) raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
64
56
|
if (!raw) {
|
|
@@ -77,11 +69,7 @@ async function confirm(opts) {
|
|
|
77
69
|
}
|
|
78
70
|
|
|
79
71
|
async function payUrl(opts) {
|
|
80
|
-
const cfg = config.
|
|
81
|
-
if (!cfg?.token) {
|
|
82
|
-
console.error("cyy: not logged in.");
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
72
|
+
const cfg = config.requireAuthSession();
|
|
85
73
|
const orderId = opts.orderId;
|
|
86
74
|
if (!orderId) {
|
|
87
75
|
console.error("cyy: --order-id required");
|
|
@@ -118,11 +106,7 @@ async function payUrl(opts) {
|
|
|
118
106
|
* @param {object} opts
|
|
119
107
|
*/
|
|
120
108
|
async function quick(opts) {
|
|
121
|
-
const cfg = config.
|
|
122
|
-
if (!cfg?.token) {
|
|
123
|
-
console.error("cyy: not logged in.");
|
|
124
|
-
process.exit(1);
|
|
125
|
-
}
|
|
109
|
+
const cfg = config.requireAuthSession();
|
|
126
110
|
|
|
127
111
|
const keyword = opts.keyword;
|
|
128
112
|
const quantity = Number(opts.quantity || 1);
|
|
@@ -329,11 +313,7 @@ async function quick(opts) {
|
|
|
329
313
|
* @param {{ page?: string, pageSize?: string, status?: string, shopId?: string, objectCode?: string, all?: boolean, shopKeyword?: string }} opts
|
|
330
314
|
*/
|
|
331
315
|
async function list(opts) {
|
|
332
|
-
const cfg = config.
|
|
333
|
-
if (!cfg?.token) {
|
|
334
|
-
console.error("cyy: not logged in.");
|
|
335
|
-
process.exit(1);
|
|
336
|
-
}
|
|
316
|
+
const cfg = config.requireAuthSession();
|
|
337
317
|
const shopId = opts.shopId != null && opts.shopId !== "" ? Number(opts.shopId) : Number(cfg.shop_id);
|
|
338
318
|
if (!Number.isFinite(shopId)) {
|
|
339
319
|
console.error("cyy: shopId missing (set shop_id in config or pass --shop-id)");
|
|
@@ -371,11 +351,7 @@ async function list(opts) {
|
|
|
371
351
|
* @param {{ orderId?: string, childId?: string }} opts
|
|
372
352
|
*/
|
|
373
353
|
async function cancel(opts) {
|
|
374
|
-
const cfg = config.
|
|
375
|
-
if (!cfg?.token) {
|
|
376
|
-
console.error("cyy: not logged in.");
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
354
|
+
const cfg = config.requireAuthSession();
|
|
379
355
|
const orderId = opts.orderId;
|
|
380
356
|
if (!orderId) {
|
|
381
357
|
console.error("cyy: --order-id required");
|
package/src/commands/product.js
CHANGED
|
@@ -9,11 +9,7 @@ const biz = require("../biz");
|
|
|
9
9
|
* @param {object} opts
|
|
10
10
|
*/
|
|
11
11
|
async function search(opts) {
|
|
12
|
-
const cfg = config.
|
|
13
|
-
if (!cfg?.token) {
|
|
14
|
-
console.error("cyy: not logged in. Run: cyy auth login");
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
12
|
+
const cfg = config.requireAuthSession();
|
|
17
13
|
|
|
18
14
|
const shopId = Number(opts.shopId ?? cfg.shop_id);
|
|
19
15
|
const siteId = Number(opts.siteId ?? cfg.site_id);
|
package/src/commands/shop.js
CHANGED
|
@@ -28,11 +28,7 @@ function envelope(success, message, upstream, exitCode) {
|
|
|
28
28
|
* @param {{ page?: string, pageSize?: string, name?: string, objectCode?: string }} opts
|
|
29
29
|
*/
|
|
30
30
|
async function list(opts) {
|
|
31
|
-
const cfg = config.
|
|
32
|
-
if (!cfg?.token) {
|
|
33
|
-
console.error("cyy: not logged in. Run: cyy auth login");
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
31
|
+
const cfg = config.requireAuthSession();
|
|
36
32
|
|
|
37
33
|
const body = JSON.stringify({
|
|
38
34
|
pageNum: Number(opts.page || 1),
|
|
@@ -62,11 +58,7 @@ async function list(opts) {
|
|
|
62
58
|
* @param {{ shopId?: string, siteName?: string }} opts
|
|
63
59
|
*/
|
|
64
60
|
async function sites(opts) {
|
|
65
|
-
const cfg = config.
|
|
66
|
-
if (!cfg?.token) {
|
|
67
|
-
console.error("cyy: not logged in. Run: cyy auth login");
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
61
|
+
const cfg = config.requireAuthSession();
|
|
70
62
|
|
|
71
63
|
const shopId = opts.shopId ?? cfg.shop_id;
|
|
72
64
|
if (shopId === undefined || shopId === null || String(shopId).trim() === "") {
|
|
@@ -179,11 +171,7 @@ async function shopIdBelongsToMember(cfg, shopId) {
|
|
|
179
171
|
* @param {{ shopId?: string }} opts
|
|
180
172
|
*/
|
|
181
173
|
async function useShop(opts) {
|
|
182
|
-
const cfg = config.
|
|
183
|
-
if (!cfg?.token) {
|
|
184
|
-
console.error("cyy: not logged in. Run: cyy auth login");
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
174
|
+
const cfg = config.requireAuthSession();
|
|
187
175
|
const shopId = opts.shopId;
|
|
188
176
|
if (shopId === undefined || shopId === null || String(shopId).trim() === "") {
|
|
189
177
|
console.error("cyy: shop use requires --shop-id");
|
|
@@ -229,11 +217,7 @@ async function useShop(opts) {
|
|
|
229
217
|
* @param {{ siteId?: string }} opts
|
|
230
218
|
*/
|
|
231
219
|
async function useSite(opts) {
|
|
232
|
-
const cfg = config.
|
|
233
|
-
if (!cfg?.token) {
|
|
234
|
-
console.error("cyy: not logged in. Run: cyy auth login");
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
220
|
+
const cfg = config.requireAuthSession();
|
|
237
221
|
const shopId = cfg.shop_id;
|
|
238
222
|
if (shopId === undefined || shopId === null || String(shopId).trim() === "") {
|
|
239
223
|
console.error("cyy: no shop_id in session; run: cyy shop use --shop-id <id>");
|
package/src/config.js
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require("fs");
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
|
|
7
|
-
const DEFAULT_BASE_URL = "https://
|
|
7
|
+
const DEFAULT_BASE_URL = "https://yunping.ifoodbuy.com/";
|
|
8
8
|
|
|
9
9
|
/** BuildConfig-style module prefixes (app-api-cli-spec §1.2) */
|
|
10
10
|
const MODULE_MAP = {
|
|
@@ -69,9 +69,71 @@ function maskToken(t) {
|
|
|
69
69
|
return `${t.slice(0, 8)}...${t.slice(-4)}`;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** Env keys that override ~/.cyymall/config.json for the current process (ephemeral). */
|
|
73
|
+
const SESSION_ENV_KEYS = [
|
|
74
|
+
["token", "CYY_TOKEN"],
|
|
75
|
+
["member_id", "CYY_MEMBER_ID"],
|
|
76
|
+
["shop_id", "CYY_SHOP_ID"],
|
|
77
|
+
["site_id", "CYY_SITE_ID"],
|
|
78
|
+
["version_code", "CYY_VERSION_CODE"],
|
|
79
|
+
["phone", "CYY_PHONE"],
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Effective session: saved config merged with CYY_* env overrides (env wins when set).
|
|
84
|
+
* @returns {Record<string, unknown>}
|
|
85
|
+
*/
|
|
86
|
+
function getSession() {
|
|
87
|
+
const file = loadConfig() || {};
|
|
88
|
+
/** @type {Record<string, unknown>} */
|
|
89
|
+
const merged = { ...file };
|
|
90
|
+
for (const [field, envKey] of SESSION_ENV_KEYS) {
|
|
91
|
+
const v = process.env[envKey];
|
|
92
|
+
if (v != null && String(v).trim() !== "") {
|
|
93
|
+
merged[field] = String(v).trim();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return merged;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Record<string, unknown>|null|undefined} [session]
|
|
101
|
+
*/
|
|
102
|
+
function hasAuthToken(session) {
|
|
103
|
+
const s = session ?? getSession();
|
|
104
|
+
const t = s?.token;
|
|
105
|
+
return t != null && String(t).trim() !== "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @returns {Record<string, unknown>}
|
|
110
|
+
*/
|
|
111
|
+
function requireAuthSession() {
|
|
112
|
+
if (!hasAuthToken()) {
|
|
113
|
+
console.error(
|
|
114
|
+
"cyy: not logged in. Use one of: cyy auth login --phone ... | cyy auth import --token ... | CYY_TOKEN=...",
|
|
115
|
+
);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
return getSession();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Names of CYY_* env vars currently overriding saved config. */
|
|
122
|
+
function getEnvSessionOverrideKeys() {
|
|
123
|
+
const active = [];
|
|
124
|
+
for (const [, envKey] of SESSION_ENV_KEYS) {
|
|
125
|
+
const v = process.env[envKey];
|
|
126
|
+
if (v != null && String(v).trim() !== "") {
|
|
127
|
+
active.push(envKey);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return active;
|
|
131
|
+
}
|
|
132
|
+
|
|
72
133
|
module.exports = {
|
|
73
134
|
DEFAULT_BASE_URL,
|
|
74
135
|
MODULE_MAP,
|
|
136
|
+
SESSION_ENV_KEYS,
|
|
75
137
|
getBaseUrl,
|
|
76
138
|
getConfigDir,
|
|
77
139
|
getConfigPath,
|
|
@@ -79,4 +141,8 @@ module.exports = {
|
|
|
79
141
|
loadConfig,
|
|
80
142
|
saveConfig,
|
|
81
143
|
maskToken,
|
|
144
|
+
getSession,
|
|
145
|
+
hasAuthToken,
|
|
146
|
+
requireAuthSession,
|
|
147
|
+
getEnvSessionOverrideKeys,
|
|
82
148
|
};
|
package/src/http.js
CHANGED
|
@@ -46,8 +46,7 @@ function buildAuthHeaders(cfg) {
|
|
|
46
46
|
|
|
47
47
|
/** Headers for unauthenticated calls (e.g. login) — still need default shop/site for gateway */
|
|
48
48
|
function buildDefaultHeaders() {
|
|
49
|
-
|
|
50
|
-
return buildAuthHeaders(cfg);
|
|
49
|
+
return buildAuthHeaders(config.getSession());
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/**
|