cyymall-cli 0.1.0 → 0.1.2
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 +14 -10
- package/bin/cyy.js +1 -1
- package/package.json +33 -33
- package/src/biz.js +14 -14
- package/src/cli.js +234 -208
- package/src/commands/apiCall.js +149 -149
- package/src/commands/auth.js +193 -19
- package/src/commands/cart.js +55 -55
- package/src/commands/order.js +399 -327
- package/src/commands/product.js +56 -56
- package/src/commands/serve.js +84 -84
- package/src/commands/shop.js +287 -287
- package/src/config.js +82 -82
- package/src/embeddedCyyKeys.js +16 -0
- package/src/encrypt.js +434 -0
- package/src/http.js +30 -3
package/src/commands/apiCall.js
CHANGED
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const config = require("../config");
|
|
4
|
-
const http = require("../http");
|
|
5
|
-
const biz = require("../biz");
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Parse repeated --query key=value
|
|
9
|
-
* @param {string[]} queryArgs
|
|
10
|
-
*/
|
|
11
|
-
function parseQueryPairs(queryArgs) {
|
|
12
|
-
const q = {};
|
|
13
|
-
if (!queryArgs || !queryArgs.length) return q;
|
|
14
|
-
for (const pair of queryArgs) {
|
|
15
|
-
const i = pair.indexOf("=");
|
|
16
|
-
if (i <= 0) continue;
|
|
17
|
-
const k = pair.slice(0, i).trim();
|
|
18
|
-
const v = pair.slice(i + 1);
|
|
19
|
-
q[k] = v;
|
|
20
|
-
}
|
|
21
|
-
return q;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function appendQuery(url, queryObj) {
|
|
25
|
-
const keys = Object.keys(queryObj);
|
|
26
|
-
if (!keys.length) return url;
|
|
27
|
-
const u = new URL(url);
|
|
28
|
-
for (const k of keys) {
|
|
29
|
-
u.searchParams.set(k, queryObj[k]);
|
|
30
|
-
}
|
|
31
|
-
return u.toString();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Shared executor for CLI and HTTP serve mode.
|
|
36
|
-
* @param {object} opts
|
|
37
|
-
*/
|
|
38
|
-
async function executeApiCall(opts) {
|
|
39
|
-
const {
|
|
40
|
-
method,
|
|
41
|
-
module: moduleKey,
|
|
42
|
-
path: pathSuffix,
|
|
43
|
-
bodyJson,
|
|
44
|
-
bodyFile,
|
|
45
|
-
bodyObj,
|
|
46
|
-
query: queryArgs,
|
|
47
|
-
queryObj: queryObjIn,
|
|
48
|
-
noAuth,
|
|
49
|
-
header: headerPairs,
|
|
50
|
-
} = opts;
|
|
51
|
-
|
|
52
|
-
let bodyStr = null;
|
|
53
|
-
if (bodyFile) {
|
|
54
|
-
const fs = require("fs");
|
|
55
|
-
bodyStr = fs.readFileSync(bodyFile, "utf8");
|
|
56
|
-
} else if (bodyObj != null) {
|
|
57
|
-
bodyStr = JSON.stringify(bodyObj);
|
|
58
|
-
} else if (bodyJson != null && bodyJson !== "") {
|
|
59
|
-
bodyStr = bodyJson;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (bodyStr && method === "GET") {
|
|
63
|
-
return { error: "GET request should not use body; use query only.", exitCode: 1 };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let url = http.moduleUrl(moduleKey, pathSuffix);
|
|
67
|
-
const queryObj =
|
|
68
|
-
queryObjIn && typeof queryObjIn === "object"
|
|
69
|
-
? queryObjIn
|
|
70
|
-
: parseQueryPairs(queryArgs || []);
|
|
71
|
-
url = appendQuery(url, queryObj);
|
|
72
|
-
|
|
73
|
-
/** @type {Record<string,string>} */
|
|
74
|
-
let headers = {};
|
|
75
|
-
|
|
76
|
-
if (!noAuth) {
|
|
77
|
-
const cfg = config.loadConfig();
|
|
78
|
-
headers = /** @type {Record<string,string>} */ (
|
|
79
|
-
http.buildAuthHeaders(cfg)
|
|
80
|
-
);
|
|
81
|
-
} else {
|
|
82
|
-
headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(null));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const extras = headerPairs || [];
|
|
86
|
-
for (const h of extras) {
|
|
87
|
-
const idx = h.indexOf(":");
|
|
88
|
-
if (idx > 0) {
|
|
89
|
-
const hk = h.slice(0, idx).trim();
|
|
90
|
-
const hv = h.slice(idx + 1).trim();
|
|
91
|
-
headers[hk] = hv;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (method === "GET" || method === "HEAD") {
|
|
96
|
-
delete headers["Content-Type"];
|
|
97
|
-
} else if (bodyStr) {
|
|
98
|
-
headers["Content-Type"] = headers["Content-Type"] || "application/json;charset=UTF-8";
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const { ok, status, json } = await http.request(url, {
|
|
102
|
-
method,
|
|
103
|
-
headers,
|
|
104
|
-
body: bodyStr,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const bizOk = ok && (json == null || biz.isBizSuccess(json));
|
|
108
|
-
const envelopeSuccess = bizOk;
|
|
109
|
-
const traceId = `local-${require("crypto").randomBytes(8).toString("hex")}`;
|
|
110
|
-
const message =
|
|
111
|
-
json && typeof json === "object" && "msg" in json
|
|
112
|
-
? String(/** @type {{msg?:string}} */ (json).msg)
|
|
113
|
-
: ok
|
|
114
|
-
? "success"
|
|
115
|
-
: `HTTP ${status}`;
|
|
116
|
-
|
|
117
|
-
const envelope = {
|
|
118
|
-
success: envelopeSuccess,
|
|
119
|
-
code: envelopeSuccess ? "OK" : "UPSTREAM_ERROR",
|
|
120
|
-
message,
|
|
121
|
-
data: { upstream: json, httpStatus: status },
|
|
122
|
-
traceId,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
return { envelope, exitCode: envelopeSuccess ? 0 : 2 };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {import('commander').OptionValues} opts
|
|
130
|
-
*/
|
|
131
|
-
async function runApiCall(opts) {
|
|
132
|
-
if (opts.bodyJson && opts.method === "GET") {
|
|
133
|
-
console.error("cyy: GET request should not use body; use --query instead.");
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
const merged = {
|
|
137
|
-
...opts,
|
|
138
|
-
noAuth: Boolean(opts.noAuth ?? opts.bare),
|
|
139
|
-
};
|
|
140
|
-
const result = await executeApiCall(merged);
|
|
141
|
-
if (result.error) {
|
|
142
|
-
console.error(`cyy: ${result.error}`);
|
|
143
|
-
process.exit(result.exitCode ?? 1);
|
|
144
|
-
}
|
|
145
|
-
console.log(JSON.stringify(result.envelope, null, 2));
|
|
146
|
-
process.exit(result.exitCode ?? 1);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
module.exports = { runApiCall, executeApiCall, parseQueryPairs };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const config = require("../config");
|
|
4
|
+
const http = require("../http");
|
|
5
|
+
const biz = require("../biz");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse repeated --query key=value
|
|
9
|
+
* @param {string[]} queryArgs
|
|
10
|
+
*/
|
|
11
|
+
function parseQueryPairs(queryArgs) {
|
|
12
|
+
const q = {};
|
|
13
|
+
if (!queryArgs || !queryArgs.length) return q;
|
|
14
|
+
for (const pair of queryArgs) {
|
|
15
|
+
const i = pair.indexOf("=");
|
|
16
|
+
if (i <= 0) continue;
|
|
17
|
+
const k = pair.slice(0, i).trim();
|
|
18
|
+
const v = pair.slice(i + 1);
|
|
19
|
+
q[k] = v;
|
|
20
|
+
}
|
|
21
|
+
return q;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function appendQuery(url, queryObj) {
|
|
25
|
+
const keys = Object.keys(queryObj);
|
|
26
|
+
if (!keys.length) return url;
|
|
27
|
+
const u = new URL(url);
|
|
28
|
+
for (const k of keys) {
|
|
29
|
+
u.searchParams.set(k, queryObj[k]);
|
|
30
|
+
}
|
|
31
|
+
return u.toString();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Shared executor for CLI and HTTP serve mode.
|
|
36
|
+
* @param {object} opts
|
|
37
|
+
*/
|
|
38
|
+
async function executeApiCall(opts) {
|
|
39
|
+
const {
|
|
40
|
+
method,
|
|
41
|
+
module: moduleKey,
|
|
42
|
+
path: pathSuffix,
|
|
43
|
+
bodyJson,
|
|
44
|
+
bodyFile,
|
|
45
|
+
bodyObj,
|
|
46
|
+
query: queryArgs,
|
|
47
|
+
queryObj: queryObjIn,
|
|
48
|
+
noAuth,
|
|
49
|
+
header: headerPairs,
|
|
50
|
+
} = opts;
|
|
51
|
+
|
|
52
|
+
let bodyStr = null;
|
|
53
|
+
if (bodyFile) {
|
|
54
|
+
const fs = require("fs");
|
|
55
|
+
bodyStr = fs.readFileSync(bodyFile, "utf8");
|
|
56
|
+
} else if (bodyObj != null) {
|
|
57
|
+
bodyStr = JSON.stringify(bodyObj);
|
|
58
|
+
} else if (bodyJson != null && bodyJson !== "") {
|
|
59
|
+
bodyStr = bodyJson;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (bodyStr && method === "GET") {
|
|
63
|
+
return { error: "GET request should not use body; use query only.", exitCode: 1 };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let url = http.moduleUrl(moduleKey, pathSuffix);
|
|
67
|
+
const queryObj =
|
|
68
|
+
queryObjIn && typeof queryObjIn === "object"
|
|
69
|
+
? queryObjIn
|
|
70
|
+
: parseQueryPairs(queryArgs || []);
|
|
71
|
+
url = appendQuery(url, queryObj);
|
|
72
|
+
|
|
73
|
+
/** @type {Record<string,string>} */
|
|
74
|
+
let headers = {};
|
|
75
|
+
|
|
76
|
+
if (!noAuth) {
|
|
77
|
+
const cfg = config.loadConfig();
|
|
78
|
+
headers = /** @type {Record<string,string>} */ (
|
|
79
|
+
http.buildAuthHeaders(cfg)
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(null));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const extras = headerPairs || [];
|
|
86
|
+
for (const h of extras) {
|
|
87
|
+
const idx = h.indexOf(":");
|
|
88
|
+
if (idx > 0) {
|
|
89
|
+
const hk = h.slice(0, idx).trim();
|
|
90
|
+
const hv = h.slice(idx + 1).trim();
|
|
91
|
+
headers[hk] = hv;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (method === "GET" || method === "HEAD") {
|
|
96
|
+
delete headers["Content-Type"];
|
|
97
|
+
} else if (bodyStr) {
|
|
98
|
+
headers["Content-Type"] = headers["Content-Type"] || "application/json;charset=UTF-8";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { ok, status, json } = await http.request(url, {
|
|
102
|
+
method,
|
|
103
|
+
headers,
|
|
104
|
+
body: bodyStr,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const bizOk = ok && (json == null || biz.isBizSuccess(json));
|
|
108
|
+
const envelopeSuccess = bizOk;
|
|
109
|
+
const traceId = `local-${require("crypto").randomBytes(8).toString("hex")}`;
|
|
110
|
+
const message =
|
|
111
|
+
json && typeof json === "object" && "msg" in json
|
|
112
|
+
? String(/** @type {{msg?:string}} */ (json).msg)
|
|
113
|
+
: ok
|
|
114
|
+
? "success"
|
|
115
|
+
: `HTTP ${status}`;
|
|
116
|
+
|
|
117
|
+
const envelope = {
|
|
118
|
+
success: envelopeSuccess,
|
|
119
|
+
code: envelopeSuccess ? "OK" : "UPSTREAM_ERROR",
|
|
120
|
+
message,
|
|
121
|
+
data: { upstream: json, httpStatus: status },
|
|
122
|
+
traceId,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return { envelope, exitCode: envelopeSuccess ? 0 : 2 };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {import('commander').OptionValues} opts
|
|
130
|
+
*/
|
|
131
|
+
async function runApiCall(opts) {
|
|
132
|
+
if (opts.bodyJson && opts.method === "GET") {
|
|
133
|
+
console.error("cyy: GET request should not use body; use --query instead.");
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const merged = {
|
|
137
|
+
...opts,
|
|
138
|
+
noAuth: Boolean(opts.noAuth ?? opts.bare),
|
|
139
|
+
};
|
|
140
|
+
const result = await executeApiCall(merged);
|
|
141
|
+
if (result.error) {
|
|
142
|
+
console.error(`cyy: ${result.error}`);
|
|
143
|
+
process.exit(result.exitCode ?? 1);
|
|
144
|
+
}
|
|
145
|
+
console.log(JSON.stringify(result.envelope, null, 2));
|
|
146
|
+
process.exit(result.exitCode ?? 1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = { runApiCall, executeApiCall, parseQueryPairs };
|
package/src/commands/auth.js
CHANGED
|
@@ -1,16 +1,77 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
const crypto = require("crypto");
|
|
4
|
+
const readline = require("readline/promises");
|
|
5
|
+
const { stdin: input, stdout: output } = require("process");
|
|
4
6
|
const config = require("../config");
|
|
5
7
|
const http = require("../http");
|
|
6
8
|
const biz = require("../biz");
|
|
9
|
+
const encrypt = require("../encrypt");
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
/**
|
|
12
|
+
* `/app/auth/ua/*` responses (e.g. sendCodeV2, registerMember/v2): raw wire -> decryptResponse ->
|
|
13
|
+
* JSON.parse -> optional parsedJsonAfterHybridEnvelopeDecrypt, same order as App.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} [rawWireText]
|
|
16
|
+
* @param {unknown} jsonAfterParse
|
|
17
|
+
* @param {string} clientPkForDecrypt
|
|
18
|
+
* @returns {unknown}
|
|
19
|
+
*/
|
|
20
|
+
function plainBizJsonFromSendCodeWire(rawWireText, jsonAfterParse, clientPkForDecrypt) {
|
|
21
|
+
let wire =
|
|
22
|
+
typeof rawWireText === "string" && rawWireText.trim()
|
|
23
|
+
? rawWireText.trim()
|
|
24
|
+
: jsonAfterParse &&
|
|
25
|
+
typeof jsonAfterParse === "object" &&
|
|
26
|
+
jsonAfterParse !== null &&
|
|
27
|
+
!Array.isArray(jsonAfterParse) &&
|
|
28
|
+
Object.prototype.hasOwnProperty.call(jsonAfterParse, "_raw") &&
|
|
29
|
+
typeof /** @type {{ _raw?: unknown }} */ (jsonAfterParse)._raw === "string"
|
|
30
|
+
? String(/** @type {{ _raw: string }} */ (jsonAfterParse)._raw).trim()
|
|
31
|
+
: "";
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
!wire &&
|
|
35
|
+
jsonAfterParse != null &&
|
|
36
|
+
typeof jsonAfterParse === "object" &&
|
|
37
|
+
!Array.isArray(jsonAfterParse) &&
|
|
38
|
+
encrypt.looksLikeHybridEnvelope(jsonAfterParse)
|
|
39
|
+
) {
|
|
40
|
+
try {
|
|
41
|
+
wire = JSON.stringify(jsonAfterParse);
|
|
42
|
+
} catch {
|
|
43
|
+
wire = "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (wire) {
|
|
48
|
+
const decryptedText = encrypt.decryptResponse(wire, clientPkForDecrypt);
|
|
49
|
+
try {
|
|
50
|
+
const obj = decryptedText ? JSON.parse(decryptedText) : null;
|
|
51
|
+
if (obj != null && typeof obj === "object" && !Array.isArray(obj)) {
|
|
52
|
+
const out = encrypt.looksLikeHybridEnvelope(obj)
|
|
53
|
+
? encrypt.parsedJsonAfterHybridEnvelopeDecrypt(obj, clientPkForDecrypt)
|
|
54
|
+
: obj;
|
|
55
|
+
if (!encrypt.looksLikeHybridEnvelope(out)) {
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// fall through to parsed-json fallback
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (jsonAfterParse != null && typeof jsonAfterParse === "object" && !Array.isArray(jsonAfterParse)) {
|
|
65
|
+
return encrypt.looksLikeHybridEnvelope(jsonAfterParse)
|
|
66
|
+
? encrypt.parsedJsonAfterHybridEnvelopeDecrypt(jsonAfterParse, clientPkForDecrypt)
|
|
67
|
+
: jsonAfterParse;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return jsonAfterParse;
|
|
10
71
|
}
|
|
11
72
|
|
|
12
73
|
/**
|
|
13
|
-
* Login `data` may use `token
|
|
74
|
+
* Login `data` may use `token`, `appToken`, or `accessToken`.
|
|
14
75
|
* @param {Record<string, unknown>|null|undefined} data
|
|
15
76
|
*/
|
|
16
77
|
function pickLoginToken(data) {
|
|
@@ -20,30 +81,118 @@ function pickLoginToken(data) {
|
|
|
20
81
|
return v != null ? String(v) : "";
|
|
21
82
|
}
|
|
22
83
|
|
|
23
|
-
|
|
84
|
+
function buildTraceId() {
|
|
85
|
+
return `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function extractBizMessage(json, fallback) {
|
|
89
|
+
return json && typeof json === "object" && "msg" in json
|
|
90
|
+
? String(/** @type {{msg?:string}} */ (json).msg || fallback)
|
|
91
|
+
: fallback;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildHybridAuthBody(plainBody, headers) {
|
|
95
|
+
if (!encrypt.encryptEnvConfigured()) {
|
|
96
|
+
return plainBody;
|
|
97
|
+
}
|
|
98
|
+
headers.encrypte = "true";
|
|
99
|
+
const { serverPublicKey } = encrypt.resolveEncryptKeys();
|
|
100
|
+
const hy = encrypt.hybridEncrypt(plainBody, serverPublicKey);
|
|
101
|
+
return encrypt.hybridEncryptResultToJson(hy);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function decryptAuthBizJson(rawWireText, jsonAfterParse) {
|
|
105
|
+
const { clientPrivateKey } = encrypt.resolveEncryptKeys();
|
|
106
|
+
if (!clientPrivateKey) return jsonAfterParse;
|
|
107
|
+
return plainBizJsonFromSendCodeWire(
|
|
108
|
+
typeof rawWireText === "string" ? rawWireText : "",
|
|
109
|
+
jsonAfterParse,
|
|
110
|
+
clientPrivateKey,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function requestSmsCode(phone) {
|
|
115
|
+
const url = http.moduleUrl("DEFAULT", "/app/auth/ua/sendCodeV2");
|
|
116
|
+
const headers = /** @type {Record<string,string>} */ (http.buildDefaultHeaders());
|
|
117
|
+
const plainBody = JSON.stringify({ phone, objectCode: "3" });
|
|
118
|
+
const body = buildHybridAuthBody(plainBody, headers);
|
|
119
|
+
|
|
120
|
+
const { ok, status, json: jsonAfterParse, rawWireText } = await http.request(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers,
|
|
123
|
+
body,
|
|
124
|
+
decryptHybridResponse: false,
|
|
125
|
+
includeRawWireText: true,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const json = decryptAuthBizJson(rawWireText, jsonAfterParse);
|
|
129
|
+
|
|
130
|
+
if (!ok) {
|
|
131
|
+
const msg = extractBizMessage(json, `send login code failed (HTTP ${status})`);
|
|
132
|
+
console.error(`cyy: ${msg}`);
|
|
133
|
+
if (json != null) console.error(JSON.stringify(json, null, 2));
|
|
134
|
+
process.exit(2);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// User-requested behavior: once the send-code request itself succeeds over HTTP,
|
|
138
|
+
// treat SMS dispatch as successful even if the encrypted response body cannot be
|
|
139
|
+
// decrypted into upstream business JSON.
|
|
140
|
+
return { status, json, acceptedByGateway: true };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function sendCode(phone) {
|
|
144
|
+
const { status, json, acceptedByGateway } = await requestSmsCode(phone);
|
|
145
|
+
const traceId = buildTraceId();
|
|
146
|
+
console.log(
|
|
147
|
+
JSON.stringify(
|
|
148
|
+
{
|
|
149
|
+
success: true,
|
|
150
|
+
code: "OK",
|
|
151
|
+
message: acceptedByGateway ? "code sent" : "code send accepted",
|
|
152
|
+
data: {
|
|
153
|
+
phone,
|
|
154
|
+
objectCode: "3",
|
|
155
|
+
httpStatus: status,
|
|
156
|
+
gatewayAccepted: acceptedByGateway,
|
|
157
|
+
upstream: json,
|
|
158
|
+
},
|
|
159
|
+
traceId,
|
|
160
|
+
},
|
|
161
|
+
null,
|
|
162
|
+
2,
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function loginWithCode(phone, code) {
|
|
24
169
|
const url = http.moduleUrl("DEFAULT", "/app/auth/ua/registerMember/v2");
|
|
25
|
-
const headers = /** @type {Record<string,string>} */ (http.
|
|
26
|
-
const
|
|
170
|
+
const headers = /** @type {Record<string,string>} */ (http.buildDefaultHeaders());
|
|
171
|
+
const plainBody = JSON.stringify({
|
|
27
172
|
phone,
|
|
28
173
|
objectCode: "3",
|
|
29
|
-
password:
|
|
30
|
-
passwordLoginFlag:
|
|
31
|
-
onKeyLoginFlag:
|
|
174
|
+
password: "",
|
|
175
|
+
passwordLoginFlag: false,
|
|
176
|
+
onKeyLoginFlag: true,
|
|
177
|
+
smsCode: code,
|
|
32
178
|
wxOpenid: "",
|
|
33
179
|
});
|
|
180
|
+
const body = buildHybridAuthBody(plainBody, headers);
|
|
34
181
|
|
|
35
|
-
const { ok, json } = await http.request(url, {
|
|
182
|
+
const { ok, json: jsonAfterParse, rawWireText } = await http.request(url, {
|
|
36
183
|
method: "POST",
|
|
37
184
|
headers,
|
|
38
185
|
body,
|
|
186
|
+
decryptHybridResponse: false,
|
|
187
|
+
includeRawWireText: true,
|
|
39
188
|
});
|
|
40
189
|
|
|
190
|
+
const json = decryptAuthBizJson(rawWireText, jsonAfterParse);
|
|
191
|
+
|
|
41
192
|
if (!ok || !biz.isBizSuccess(json)) {
|
|
42
|
-
const msg =
|
|
43
|
-
json && typeof json === "object" && "msg" in json
|
|
44
|
-
? String(/** @type {{msg?:string}} */ (json).msg)
|
|
45
|
-
: "login failed";
|
|
193
|
+
const msg = extractBizMessage(json, "login failed");
|
|
46
194
|
console.error(`cyy: ${msg}`);
|
|
195
|
+
if (json != null) console.error(JSON.stringify(json, null, 2));
|
|
47
196
|
process.exit(2);
|
|
48
197
|
}
|
|
49
198
|
|
|
@@ -74,7 +223,7 @@ async function login(phone, password) {
|
|
|
74
223
|
|
|
75
224
|
config.saveConfig(saved);
|
|
76
225
|
|
|
77
|
-
const traceId =
|
|
226
|
+
const traceId = buildTraceId();
|
|
78
227
|
console.log(
|
|
79
228
|
JSON.stringify(
|
|
80
229
|
{
|
|
@@ -98,10 +247,35 @@ async function login(phone, password) {
|
|
|
98
247
|
process.exit(0);
|
|
99
248
|
}
|
|
100
249
|
|
|
250
|
+
async function promptCode() {
|
|
251
|
+
const rl = readline.createInterface({ input, output });
|
|
252
|
+
try {
|
|
253
|
+
const answer = await rl.question("Enter SMS verification code: ");
|
|
254
|
+
return String(answer || "").trim();
|
|
255
|
+
} finally {
|
|
256
|
+
rl.close();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function login(phone, code) {
|
|
261
|
+
if (code) {
|
|
262
|
+
await loginWithCode(phone, code);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
await requestSmsCode(phone);
|
|
267
|
+
const smsCode = await promptCode();
|
|
268
|
+
if (!smsCode) {
|
|
269
|
+
console.error("cyy: verification code is required");
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
await loginWithCode(phone, smsCode);
|
|
273
|
+
}
|
|
274
|
+
|
|
101
275
|
async function whoami() {
|
|
102
276
|
const cfg = config.loadConfig();
|
|
103
277
|
if (!cfg?.token) {
|
|
104
|
-
console.error("cyy: not logged in. Run: cyy auth login --phone ...
|
|
278
|
+
console.error("cyy: not logged in. Run: cyy auth login --phone ...");
|
|
105
279
|
process.exit(1);
|
|
106
280
|
}
|
|
107
281
|
const url = http.moduleUrl("DEFAULT", "/member/getInfo/V2");
|
|
@@ -113,7 +287,7 @@ async function whoami() {
|
|
|
113
287
|
body: null,
|
|
114
288
|
});
|
|
115
289
|
|
|
116
|
-
const traceId =
|
|
290
|
+
const traceId = buildTraceId();
|
|
117
291
|
const success = ok && biz.isBizSuccess(json);
|
|
118
292
|
console.log(
|
|
119
293
|
JSON.stringify(
|
|
@@ -131,4 +305,4 @@ async function whoami() {
|
|
|
131
305
|
process.exit(success ? 0 : 2);
|
|
132
306
|
}
|
|
133
307
|
|
|
134
|
-
module.exports = { login, whoami };
|
|
308
|
+
module.exports = { login, loginWithCode, sendCode, whoami };
|
package/src/commands/cart.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const crypto = require("crypto");
|
|
5
|
-
const http = require("../http");
|
|
6
|
-
const config = require("../config");
|
|
7
|
-
const biz = require("../biz");
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
11
|
-
*/
|
|
12
|
-
async function add(opts) {
|
|
13
|
-
const cfg = config.loadConfig();
|
|
14
|
-
if (!cfg?.token) {
|
|
15
|
-
console.error("cyy: not logged in.");
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let raw = opts.bodyJson;
|
|
20
|
-
if (opts.bodyFile) {
|
|
21
|
-
raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
22
|
-
}
|
|
23
|
-
if (!raw) {
|
|
24
|
-
console.error("cyy: provide --body-file or --body-json");
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const url = http.moduleUrl("ORDER", "/app/order/cart");
|
|
29
|
-
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
30
|
-
|
|
31
|
-
const { ok, json } = await http.request(url, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers,
|
|
34
|
-
body: raw,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
38
|
-
const success = ok && biz.isBizSuccess(json);
|
|
39
|
-
console.log(
|
|
40
|
-
JSON.stringify(
|
|
41
|
-
{
|
|
42
|
-
success,
|
|
43
|
-
code: success ? "OK" : "UPSTREAM_ERROR",
|
|
44
|
-
message: success ? "success" : "cart failed",
|
|
45
|
-
data: { upstream: json },
|
|
46
|
-
traceId,
|
|
47
|
-
},
|
|
48
|
-
null,
|
|
49
|
-
2,
|
|
50
|
-
),
|
|
51
|
-
);
|
|
52
|
-
process.exit(success ? 0 : 2);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = { add };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const http = require("../http");
|
|
6
|
+
const config = require("../config");
|
|
7
|
+
const biz = require("../biz");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {{ bodyFile?: string, bodyJson?: string }} opts
|
|
11
|
+
*/
|
|
12
|
+
async function add(opts) {
|
|
13
|
+
const cfg = config.loadConfig();
|
|
14
|
+
if (!cfg?.token) {
|
|
15
|
+
console.error("cyy: not logged in.");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let raw = opts.bodyJson;
|
|
20
|
+
if (opts.bodyFile) {
|
|
21
|
+
raw = fs.readFileSync(opts.bodyFile, "utf8");
|
|
22
|
+
}
|
|
23
|
+
if (!raw) {
|
|
24
|
+
console.error("cyy: provide --body-file or --body-json");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const url = http.moduleUrl("ORDER", "/app/order/cart");
|
|
29
|
+
const headers = /** @type {Record<string,string>} */ (http.buildAuthHeaders(cfg));
|
|
30
|
+
|
|
31
|
+
const { ok, json } = await http.request(url, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers,
|
|
34
|
+
body: raw,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const traceId = `local-${crypto.randomBytes(8).toString("hex")}`;
|
|
38
|
+
const success = ok && biz.isBizSuccess(json);
|
|
39
|
+
console.log(
|
|
40
|
+
JSON.stringify(
|
|
41
|
+
{
|
|
42
|
+
success,
|
|
43
|
+
code: success ? "OK" : "UPSTREAM_ERROR",
|
|
44
|
+
message: success ? "success" : "cart failed",
|
|
45
|
+
data: { upstream: json },
|
|
46
|
+
traceId,
|
|
47
|
+
},
|
|
48
|
+
null,
|
|
49
|
+
2,
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
process.exit(success ? 0 : 2);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { add };
|