@lark-apaas/miaoda-cli 0.1.3-alpha.70f9b2d → 0.1.3-alpha.848d934
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/dist/api/db/api.js +3 -1
- package/dist/api/db/client.js +65 -32
- package/dist/cli/handlers/db/recovery.js +7 -10
- package/package.json +1 -1
package/dist/api/db/api.js
CHANGED
|
@@ -436,7 +436,9 @@ async function listAuditLog(opts) {
|
|
|
436
436
|
}
|
|
437
437
|
const client = (0, http_1.getHttpClient)();
|
|
438
438
|
const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/audit/log', {
|
|
439
|
-
tables
|
|
439
|
+
// IDL `Tables list<string>` 走重复 query key(?tables=A&tables=B)—— join(',')
|
|
440
|
+
// 会让 Hertz/Kitex 网关绑成 ["A,B"] 单元素切片,命中单表错误分支报错文案错乱。
|
|
441
|
+
tables: opts.tables,
|
|
440
442
|
since: opts.since,
|
|
441
443
|
until: opts.until,
|
|
442
444
|
limit: opts.limit !== undefined ? String(opts.limit) : undefined,
|
package/dist/api/db/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SQLSTATE_MAP = void 0;
|
|
3
|
+
exports.SQLSTATE_MAP = exports.ENV_CTX_TYPO = void 0;
|
|
4
4
|
exports.traceHttp = traceHttp;
|
|
5
5
|
exports.ensureInnerSuccess = ensureInnerSuccess;
|
|
6
6
|
exports.mapDataloomBizError = mapDataloomBizError;
|
|
@@ -71,7 +71,8 @@ function ensureInnerSuccess(body) {
|
|
|
71
71
|
*
|
|
72
72
|
* 优先级:
|
|
73
73
|
* 1. k_dl_1300002 + SQLSTATE 透传 → SQLSTATE_MAP
|
|
74
|
-
* 2. k_dl_1600000 + "Invalid DB Branch" →
|
|
74
|
+
* 2. k_dl_1600000 + "Invalid DB Branch" → UNKNOWN_ENV_VALUE 占位(mapDbHttpError
|
|
75
|
+
* 会用用户实际 --env 值通过 decorateEnvError 重写)
|
|
75
76
|
* 3. BIZ_ERR_MAP 命中 → 语义 code(可能带 hint)
|
|
76
77
|
* 4. 兜底 DB_API_<code>
|
|
77
78
|
*
|
|
@@ -102,51 +103,70 @@ function mapDataloomBizError(code, rawMessage) {
|
|
|
102
103
|
}
|
|
103
104
|
return new error_1.AppError(`DB_API_${code}`, message);
|
|
104
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* typo / CLI vocab violation 路径用:state 未知,列双 env 让用户在 dev/online 之间选,
|
|
108
|
+
* 不引导 init(init 不会创建 typo 出来的分支)。
|
|
109
|
+
*/
|
|
110
|
+
exports.ENV_CTX_TYPO = {
|
|
111
|
+
available: ['dev', 'online'],
|
|
112
|
+
suggestInit: false,
|
|
113
|
+
};
|
|
105
114
|
/**
|
|
106
115
|
* 统一构造"--env 值不可用"错误。两类入口共用:
|
|
107
116
|
* - CLI vocab 校验(_env.ts validateEnv):用户传 dev/main/online 以外值
|
|
108
117
|
* - backend state 错(decorateEnvError):vocab 合法但 db 实际状态对不上
|
|
109
|
-
* 两者用户角度都是「这个 env 不存在」,文案统一避免在两种失败之间困惑。
|
|
110
118
|
*
|
|
111
|
-
* `available`
|
|
112
|
-
*
|
|
119
|
+
* `available` 跟 `suggestInit` 由调用方按错误码归类传入;user-facing 不展示 `main`
|
|
120
|
+
* 内部别名,统一用 `online`。
|
|
113
121
|
*/
|
|
114
|
-
function buildUnknownEnvError(env) {
|
|
115
|
-
const
|
|
122
|
+
function buildUnknownEnvError(env, ctx = exports.ENV_CTX_TYPO) {
|
|
123
|
+
const hints = [
|
|
124
|
+
`--env must match an existing database branch. Available: ${ctx.available.join(', ')}.`,
|
|
125
|
+
];
|
|
126
|
+
if (ctx.suggestInit) {
|
|
127
|
+
hints.push(`Run \`miaoda db migration init\` before using '${env}'.`);
|
|
128
|
+
}
|
|
116
129
|
return new error_1.AppError('UNKNOWN_ENV_VALUE', `Unknown --env value '${env}'`, {
|
|
117
|
-
next_actions:
|
|
118
|
-
`--env must match an existing database branch. Available: ${available}.`,
|
|
119
|
-
`Run \`miaoda db migration init\` before using '${env}'.`,
|
|
120
|
-
],
|
|
130
|
+
next_actions: hints,
|
|
121
131
|
});
|
|
122
132
|
}
|
|
123
133
|
/**
|
|
124
|
-
* 把 backend env 相关错误统一重写成 UNKNOWN_ENV_VALUE
|
|
134
|
+
* 把 backend env 相关错误统一重写成 UNKNOWN_ENV_VALUE 文案,按错误码做 state 推断:
|
|
125
135
|
*
|
|
126
|
-
*
|
|
127
|
-
* -
|
|
128
|
-
*
|
|
129
|
-
* -
|
|
130
|
-
* - DB_API_k_dl_000007 + "db branch not found":pg connection 层 ErrParamsInvalid
|
|
136
|
+
* - DB_API_k_dl_1300033(非专家应用)→ 单 env (online),不建议 init
|
|
137
|
+
* - DB_API_k_dl_1300039(专家未 init)→ 单 env (online),建议 init
|
|
138
|
+
* - DB_API_k_dl_000007 + "db branch not found"(pg connection 兜底)→ 单 env,建议 init
|
|
139
|
+
* - UNKNOWN_ENV_VALUE(mapDataloomBizError 对 k_dl_1600000 的占位)→ typo path
|
|
131
140
|
*
|
|
132
|
-
* env 未传时不改写:避免把 backend
|
|
133
|
-
*
|
|
141
|
+
* env 未传时不改写:避免把 backend 兜底文案(如 "Multi-env is not initialized")
|
|
142
|
+
* 改成 `Unknown --env value ''` 这种空字符串错误。
|
|
134
143
|
*/
|
|
135
144
|
function decorateEnvError(err, env) {
|
|
136
145
|
if (env === undefined || env === '')
|
|
137
146
|
return err;
|
|
138
|
-
|
|
147
|
+
const ctx = classifyEnvError(err);
|
|
148
|
+
if (!ctx)
|
|
139
149
|
return err;
|
|
140
|
-
return buildUnknownEnvError(env);
|
|
150
|
+
return buildUnknownEnvError(env, ctx);
|
|
141
151
|
}
|
|
142
|
-
function
|
|
143
|
-
if (err.code === '
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (err.code === '
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
function classifyEnvError(err) {
|
|
153
|
+
if (err.code === 'DB_API_k_dl_1300033') {
|
|
154
|
+
// 非专家应用:init 也会被同一 guard 拒掉,建议 init 是误导
|
|
155
|
+
return { available: ['online'], suggestInit: false };
|
|
156
|
+
}
|
|
157
|
+
if (err.code === 'DB_API_k_dl_1300039') {
|
|
158
|
+
// 专家未 init:init 会创建 dev 分支
|
|
159
|
+
return { available: ['online'], suggestInit: true };
|
|
160
|
+
}
|
|
161
|
+
if (err.code === 'DB_API_k_dl_000007' && /db branch not found/i.test(err.message)) {
|
|
162
|
+
// pg connection 层 ErrParamsInvalid:大概率 pre-init;建议 init(非专家会被 init 路径拦)
|
|
163
|
+
return { available: ['online'], suggestInit: true };
|
|
164
|
+
}
|
|
165
|
+
if (err.code === 'UNKNOWN_ENV_VALUE') {
|
|
166
|
+
// mapDataloomBizError 对 k_dl_1600000 的占位:typo path,state 未知,列双 env
|
|
167
|
+
return exports.ENV_CTX_TYPO;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
150
170
|
}
|
|
151
171
|
/**
|
|
152
172
|
* 剥掉 PG 透传错误的 "ERROR:" 前缀:CLI 输出本身就会前缀 "Error:",
|
|
@@ -255,12 +275,25 @@ function buildInnerUrl(appId, path, query) {
|
|
|
255
275
|
let url = `/v1/dataloom/app/${encodeURIComponent(appId)}${normalized}`;
|
|
256
276
|
if (query) {
|
|
257
277
|
const usp = new URLSearchParams();
|
|
258
|
-
for (const [k,
|
|
259
|
-
if (
|
|
278
|
+
for (const [k, rawV] of Object.entries(query)) {
|
|
279
|
+
if (rawV === undefined)
|
|
280
|
+
continue;
|
|
281
|
+
// 数组值 → 重复 key 编码(?k=v1&k=v2)。IDL 里 list<string> 类型经
|
|
282
|
+
// Hertz/Kitex HTTP→Thrift 网关绑定 slice 字段需要重复 key;如果改成
|
|
283
|
+
// join(',') 单串,后端会拿到 ["v1,v2"] 单元素切片而非 ["v1","v2"]。
|
|
284
|
+
if (typeof rawV !== 'string') {
|
|
285
|
+
for (const item of rawV) {
|
|
286
|
+
if (item === '')
|
|
287
|
+
continue;
|
|
288
|
+
usp.append(k, item);
|
|
289
|
+
}
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (rawV === '')
|
|
260
293
|
continue;
|
|
261
294
|
// dbBranch 兼容用户视角的 `online` 别名 → 后端实际 dbBranch 名为 `main`,
|
|
262
295
|
// 这里集中归一,避免每个 API 函数各自处理。其他值(dev / 自定义分支)原样透传。
|
|
263
|
-
const norm = k === 'dbBranch' &&
|
|
296
|
+
const norm = k === 'dbBranch' && rawV === 'online' ? 'main' : rawV;
|
|
264
297
|
usp.append(k, norm);
|
|
265
298
|
}
|
|
266
299
|
const qs = usp.toString();
|
|
@@ -203,20 +203,17 @@ async function runRecoveryPreview(appId, ts, rawTarget) {
|
|
|
203
203
|
}
|
|
204
204
|
// ── helpers ──
|
|
205
205
|
/**
|
|
206
|
-
* 把用户传入的 recovery target
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
206
|
+
* 把用户传入的 recovery target 解析成本地时区的 ISO 8601 字符串,
|
|
207
|
+
* 如 "2026-05-20T22:32:00+08:00"。无论用户传相对值(30m / 2h / 3d)还是绝对值,
|
|
208
|
+
* 都统一展示成"具体几点",让用户一眼看清楚"目标恢复点是我本地几点"——
|
|
209
|
+
* "30m ago"这种相对描述虽然原样直观,但跟 backend dataloom 收到的实际时间点没有
|
|
210
|
+
* 显式对照,遇到长 poll 时间过去后会让人怀疑"算的到底是哪个点"。
|
|
211
211
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
212
|
+
* 不用 UTC 'Z' 后缀:用户读时间不直观;带 +/-HH:MM offset 一眼即懂时差。
|
|
213
|
+
* 解析失败 → 原样返回兜底,避免 spinner 渲染崩。
|
|
214
214
|
*/
|
|
215
215
|
function formatRecoveryTargetForDisplay(rawInput) {
|
|
216
216
|
const trimmed = rawInput.trim();
|
|
217
|
-
if (/^\d+[smhdw]$/i.test(trimmed)) {
|
|
218
|
-
return `${trimmed} ago`;
|
|
219
|
-
}
|
|
220
217
|
let ms;
|
|
221
218
|
try {
|
|
222
219
|
ms = (0, time_1.parseTimeToMs)(trimmed);
|