@lark-apaas/miaoda-cli 0.1.3-alpha.4bf312e → 0.1.3-alpha.4ec3549

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.
Files changed (64) hide show
  1. package/dist/api/app/api.js +3 -3
  2. package/dist/api/app/schemas.js +43 -43
  3. package/dist/api/db/api.js +132 -112
  4. package/dist/api/db/client.js +41 -41
  5. package/dist/api/db/index.js +2 -1
  6. package/dist/api/db/parsers.js +20 -20
  7. package/dist/api/db/sql-keywords.js +87 -87
  8. package/dist/api/deploy/api.js +5 -5
  9. package/dist/api/deploy/schemas.js +32 -32
  10. package/dist/api/file/api.js +74 -87
  11. package/dist/api/file/client.js +62 -22
  12. package/dist/api/file/detect.js +3 -3
  13. package/dist/api/file/parsers.js +18 -7
  14. package/dist/api/observability/api.js +6 -6
  15. package/dist/api/observability/schemas.js +14 -14
  16. package/dist/api/plugin/api.js +31 -31
  17. package/dist/cli/commands/app/index.js +12 -12
  18. package/dist/cli/commands/db/index.js +268 -163
  19. package/dist/cli/commands/deploy/index.js +28 -28
  20. package/dist/cli/commands/file/index.js +81 -61
  21. package/dist/cli/commands/observability/index.js +69 -69
  22. package/dist/cli/commands/plugin/index.js +27 -27
  23. package/dist/cli/commands/shared.js +10 -10
  24. package/dist/cli/handlers/app/update.js +2 -2
  25. package/dist/cli/handlers/db/_operator.js +35 -0
  26. package/dist/cli/handlers/db/audit.js +104 -53
  27. package/dist/cli/handlers/db/changelog.js +28 -13
  28. package/dist/cli/handlers/db/data.js +31 -30
  29. package/dist/cli/handlers/db/migration.js +27 -27
  30. package/dist/cli/handlers/db/quota.js +3 -3
  31. package/dist/cli/handlers/db/recovery.js +138 -98
  32. package/dist/cli/handlers/db/schema.js +33 -33
  33. package/dist/cli/handlers/db/sql.js +69 -69
  34. package/dist/cli/handlers/deploy/deploy.js +4 -4
  35. package/dist/cli/handlers/deploy/error-log.js +1 -1
  36. package/dist/cli/handlers/deploy/get.js +3 -3
  37. package/dist/cli/handlers/deploy/polling.js +11 -11
  38. package/dist/cli/handlers/file/cp.js +30 -30
  39. package/dist/cli/handlers/file/ls.js +5 -5
  40. package/dist/cli/handlers/file/quota.js +2 -2
  41. package/dist/cli/handlers/file/rm.js +24 -24
  42. package/dist/cli/handlers/file/sign.js +3 -3
  43. package/dist/cli/handlers/file/stat.js +10 -9
  44. package/dist/cli/handlers/observability/analytics.js +47 -47
  45. package/dist/cli/handlers/observability/helpers.js +2 -2
  46. package/dist/cli/handlers/observability/log.js +9 -9
  47. package/dist/cli/handlers/observability/metric.js +26 -26
  48. package/dist/cli/handlers/observability/trace.js +5 -5
  49. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  50. package/dist/cli/handlers/plugin/plugin.js +15 -15
  51. package/dist/cli/help.js +16 -16
  52. package/dist/main.js +12 -12
  53. package/dist/utils/args.js +1 -1
  54. package/dist/utils/colors.js +2 -2
  55. package/dist/utils/config.js +2 -2
  56. package/dist/utils/devops-error.js +9 -9
  57. package/dist/utils/error.js +2 -2
  58. package/dist/utils/git.js +4 -4
  59. package/dist/utils/http.js +19 -19
  60. package/dist/utils/output.js +47 -47
  61. package/dist/utils/poll.js +2 -2
  62. package/dist/utils/render.js +27 -27
  63. package/dist/utils/time.js +47 -42
  64. package/package.json +1 -1
@@ -13,47 +13,34 @@ exports.migrate = migrate;
13
13
  exports.getMigrationStatus = getMigrationStatus;
14
14
  exports.recover = recover;
15
15
  exports.getRecoveryPreview = getRecoveryPreview;
16
+ exports.getRecoveryStatus = getRecoveryStatus;
16
17
  exports.getDbQuota = getDbQuota;
17
18
  const http_1 = require("../../utils/http");
18
19
  const error_1 = require("../../utils/error");
19
20
  const http_client_1 = require("@lark-apaas/http-client");
20
21
  const client_1 = require("./client");
21
- /**
22
- * SDK 抛出的 HttpError 统一映射成 CLI 层错误:
23
- * 1. 先尝试从 response body 解 envelope,命中 dataloom 业务 code → AppError
24
- * 2. 兜底返 HttpError,保留真实 status 码与上下文
25
- *
26
- * 配合调用点的 try/catch + traceHttp,让 --verbose 在错误路径上也能拿到
27
- * x-tt-logid 与 status,方便定位线上问题。
28
- */
29
- async function mapDbHttpError(err, url, ctx,
30
- /**
31
- * 可选 hook:解析到响应 body 后,如果 envelope 命中业务错误并抛出 AppError,
32
- * 调用方可以借此把 body 里的额外字段(如 multi-statement 的 partial results)
33
- * 挂到 AppError 上。
34
- */
35
- onErrorBody) {
22
+ const DEFAULT_TIMEOUT_HINT = 'The server may still be processing your request. Retry the command if needed.';
23
+ async function mapDbHttpError(err, url, ctx, opts) {
36
24
  if (err instanceof error_1.AppError)
37
25
  throw err;
38
26
  // 客户端超时 / abort:SdkHttpError 时 response 通常缺失(status=0),落到 throw 时
39
- // message 形如 'Failed to recover: 0' 让用户看不懂。识别 abort / timeout 关键字
40
- // 转成专用错误码 + 友好 hint
27
+ // message 形如 'Failed to recover: 0' 让用户看不懂。识别 abort / timeout 关键字后
28
+ // 单独抛专用 AppError(带领域可定制的 message + hint),不再包成通用 "Failed to X"
41
29
  if (err instanceof Error) {
42
- const msg = err.message.toLowerCase();
43
- if (msg.includes("aborted") ||
44
- msg.includes("timeout") ||
45
- err.name === "AbortError" ||
46
- err.name === "TimeoutError") {
47
- throw new error_1.AppError("REQUEST_TIMEOUT", `${ctx}: request timed out`, {
48
- next_actions: [
49
- "Server-side async tasks may take up to 60s. Retry the command if the underlying task likely succeeded.",
50
- ],
51
- });
30
+ const lower = err.message.toLowerCase();
31
+ if (lower.includes('aborted') ||
32
+ lower.includes('timeout') ||
33
+ err.name === 'AbortError' ||
34
+ err.name === 'TimeoutError') {
35
+ const code = opts?.timeout?.code ?? 'REQUEST_TIMEOUT';
36
+ const message = opts?.timeout?.message ?? 'Request timed out after 30s';
37
+ const hint = opts?.timeout?.hint ?? DEFAULT_TIMEOUT_HINT;
38
+ throw new error_1.AppError(code, message, { next_actions: [hint] });
52
39
  }
53
40
  }
54
41
  if (err instanceof http_client_1.HttpError) {
55
42
  const status = err.response?.status ?? 0;
56
- const statusText = err.response?.statusText ?? "";
43
+ const statusText = err.response?.statusText ?? '';
57
44
  try {
58
45
  const body = (await err.response?.json());
59
46
  if (body) {
@@ -61,8 +48,8 @@ onErrorBody) {
61
48
  (0, client_1.extractData)(body); // 业务 code 命中 → 抛 AppError;不命中走兜底
62
49
  }
63
50
  catch (appErr) {
64
- if (appErr instanceof error_1.AppError && onErrorBody) {
65
- onErrorBody(body, appErr);
51
+ if (appErr instanceof error_1.AppError && opts?.onErrorBody) {
52
+ opts.onErrorBody(body, appErr);
66
53
  }
67
54
  throw appErr;
68
55
  }
@@ -105,18 +92,28 @@ function attachSqlPartialResults(body, appErr) {
105
92
  */
106
93
  async function execSql(opts) {
107
94
  const client = (0, http_1.getHttpClient)();
108
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/sql", {
95
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/sql', {
109
96
  dbBranch: opts.dbBranch,
110
97
  });
111
98
  const start = Date.now();
112
99
  let response;
113
100
  try {
114
101
  response = await client.post(url, { sql: opts.sql });
115
- (0, client_1.traceHttp)("POST", url, start, response);
102
+ (0, client_1.traceHttp)('POST', url, start, response);
116
103
  }
117
104
  catch (err) {
118
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
119
- await mapDbHttpError(err, url, "Failed to execute SQL", attachSqlPartialResults);
105
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
106
+ await mapDbHttpError(err, url, 'Failed to execute SQL', {
107
+ onErrorBody: attachSqlPartialResults,
108
+ // SQL 路径单独的超时文案:PG 的 statement_timeout 会回滚整条事务,所以这里
109
+ // 明示「事务已回滚、没有改动落地」,并把 hint 引导到「简化 SQL / 加 LIMIT / 拆条」。
110
+ timeout: {
111
+ code: 'SQL_EXECUTION_TIMEOUT',
112
+ message: 'SQL execution timed out after 30s',
113
+ hint: 'The transaction was rolled back; no changes were applied. ' +
114
+ 'Simplify the SQL, add filters or LIMIT for queries, or split it into smaller statements.',
115
+ },
116
+ });
120
117
  throw err; // 不可达
121
118
  }
122
119
  const body = (await response.json());
@@ -127,11 +124,11 @@ async function execSql(opts) {
127
124
  // results 末尾追加一条 SqlType="ERROR" 的哨兵,data 字段是 {code,message} JSON。
128
125
  // 这样网关在错误路径不会吞业务字段,CLI 拿到完整 partial_results + statement_index。
129
126
  const last = results.at(-1);
130
- if (last?.sqlType === "ERROR") {
127
+ if (last?.sqlType === 'ERROR') {
131
128
  const appErr = parseSqlErrorSentinel(last.data);
132
129
  appErr.partial_results = results.slice(0, -1);
133
130
  const stmtIdx = data.errorStatementIndex;
134
- if (typeof stmtIdx === "number")
131
+ if (typeof stmtIdx === 'number')
135
132
  appErr.statement_index = stmtIdx;
136
133
  throw appErr;
137
134
  }
@@ -163,12 +160,12 @@ async function execSql(opts) {
163
160
  function parseSqlErrorSentinel(payload) {
164
161
  try {
165
162
  const parsed = JSON.parse(payload);
166
- const code = typeof parsed.code === "string" && parsed.code !== "" ? parsed.code : "INTERNAL_DB_ERROR";
167
- const message = typeof parsed.message === "string" ? parsed.message : payload;
163
+ const code = typeof parsed.code === 'string' && parsed.code !== '' ? parsed.code : 'INTERNAL_DB_ERROR';
164
+ const message = typeof parsed.message === 'string' ? parsed.message : payload;
168
165
  return (0, client_1.mapDataloomBizError)(code, message);
169
166
  }
170
167
  catch {
171
- return new error_1.AppError("INTERNAL_DB_ERROR", payload);
168
+ return new error_1.AppError('INTERNAL_DB_ERROR', payload);
172
169
  }
173
170
  }
174
171
  // ── db schema → InnerGetSchema ──
@@ -180,21 +177,21 @@ function parseSqlErrorSentinel(payload) {
180
177
  */
181
178
  async function getSchema(opts) {
182
179
  const client = (0, http_1.getHttpClient)();
183
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/schema", {
184
- format: opts.format ?? "schema",
180
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/schema', {
181
+ format: opts.format ?? 'schema',
185
182
  tableNames: opts.tableNames,
186
- includeStats: opts.includeStats ? "true" : undefined,
183
+ includeStats: opts.includeStats ? 'true' : undefined,
187
184
  dbBranch: opts.dbBranch,
188
185
  });
189
186
  const start = Date.now();
190
187
  let response;
191
188
  try {
192
189
  response = await client.get(url);
193
- (0, client_1.traceHttp)("GET", url, start, response);
190
+ (0, client_1.traceHttp)('GET', url, start, response);
194
191
  }
195
192
  catch (err) {
196
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
197
- await mapDbHttpError(err, url, "Failed to get schema");
193
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
194
+ await mapDbHttpError(err, url, 'Failed to get schema');
198
195
  throw err; // 不可达
199
196
  }
200
197
  const body = (await response.json());
@@ -214,24 +211,24 @@ async function getSchema(opts) {
214
211
  */
215
212
  async function importData(opts) {
216
213
  const client = (0, http_1.getHttpClient)();
217
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/data/import");
214
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/data/import');
218
215
  const reqBody = {
219
216
  tableName: opts.tableName,
220
217
  format: opts.format,
221
- records: opts.body.toString("utf8"),
218
+ records: opts.body.toString('utf8'),
222
219
  };
223
- if (opts.dbBranch !== undefined && opts.dbBranch !== "") {
220
+ if (opts.dbBranch !== undefined && opts.dbBranch !== '') {
224
221
  reqBody.dbBranch = opts.dbBranch;
225
222
  }
226
223
  const start = Date.now();
227
224
  let response;
228
225
  try {
229
226
  response = await client.post(url, reqBody);
230
- (0, client_1.traceHttp)("POST", url, start, response);
227
+ (0, client_1.traceHttp)('POST', url, start, response);
231
228
  }
232
229
  catch (err) {
233
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
234
- await mapDbHttpError(err, url, "Failed to import data");
230
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
231
+ await mapDbHttpError(err, url, 'Failed to import data');
235
232
  throw err; // 不可达
236
233
  }
237
234
  // 后端 InnerAdminImportData 响应里 data 直接返 {tableName, recordCount, durationMs}
@@ -255,7 +252,7 @@ async function importData(opts) {
255
252
  */
256
253
  async function exportData(opts) {
257
254
  const client = (0, http_1.getHttpClient)();
258
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/data/export", {
255
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/data/export', {
259
256
  tableName: opts.tableName,
260
257
  format: opts.format,
261
258
  limit: String(opts.limit ?? 5000),
@@ -265,35 +262,35 @@ async function exportData(opts) {
265
262
  const start = Date.now();
266
263
  let response;
267
264
  try {
268
- response = await client.request({ method: "POST", url });
269
- (0, client_1.traceHttp)("POST", url, start, response);
265
+ response = await client.request({ method: 'POST', url });
266
+ (0, client_1.traceHttp)('POST', url, start, response);
270
267
  }
271
268
  catch (err) {
272
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
273
- await mapDbHttpError(err, url, "Failed to export data");
269
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
270
+ await mapDbHttpError(err, url, 'Failed to export data');
274
271
  throw err; // 不可达
275
272
  }
276
273
  // 成功路径:响应 body 通常是原始 CSV/SQL/JSON 字节,但部分错误场景下网关会返
277
274
  // HTTP 200 + JSON envelope(status_code != "0"),需要在这里嗅探兜底。
278
275
  const defaultContentType = {
279
- csv: "text/csv",
280
- sql: "text/plain",
281
- json: "application/json",
276
+ csv: 'text/csv',
277
+ sql: 'text/plain',
278
+ json: 'application/json',
282
279
  };
283
- const contentType = response.headers.get("Content-Type") ?? defaultContentType[opts.format];
280
+ const contentType = response.headers.get('Content-Type') ?? defaultContentType[opts.format];
284
281
  const ab = await response.arrayBuffer();
285
282
  const buf = Buffer.from(new Uint8Array(ab));
286
283
  if (buf.length === 0) {
287
- throw new error_1.AppError("INTERNAL_DB_ERROR", "Empty export response body");
284
+ throw new error_1.AppError('INTERNAL_DB_ERROR', 'Empty export response body');
288
285
  }
289
286
  // Envelope sniff:HTTP 200 + Content-Type 为 application/json + body 解析得到
290
287
  // InnerEnvelope 且 status_code 非 "0" 时,按业务错误抛出。
291
288
  // CSV / SQL 都是非 JSON 输出,application/json 响应必是错误信封;JSON 格式
292
289
  // 成功响应自身就是 application/json,跳过 sniff 避免误判。
293
- if (opts.format !== "json" && /application\/json/i.test(contentType)) {
290
+ if (opts.format !== 'json' && /application\/json/i.test(contentType)) {
294
291
  try {
295
- const parsed = JSON.parse(buf.toString("utf8"));
296
- if (parsed.status_code != null && parsed.status_code !== "0") {
292
+ const parsed = JSON.parse(buf.toString('utf8'));
293
+ if (parsed.status_code != null && parsed.status_code !== '0') {
297
294
  // 复用 extractData 的错误映射逻辑(throw AppError)
298
295
  (0, client_1.extractData)(parsed);
299
296
  }
@@ -306,7 +303,7 @@ async function exportData(opts) {
306
303
  }
307
304
  }
308
305
  // 后端通过响应头回传记录数(避免污染 body);header 缺失或解析失败 → undefined
309
- const recordCountHeader = response.headers.get("X-Miaoda-Record-Count");
306
+ const recordCountHeader = response.headers.get('X-Miaoda-Record-Count');
310
307
  const parsedCount = recordCountHeader != null ? Number(recordCountHeader) : NaN;
311
308
  const recordCount = Number.isFinite(parsedCount) && parsedCount >= 0 ? parsedCount : undefined;
312
309
  return {
@@ -325,7 +322,7 @@ async function exportData(opts) {
325
322
  */
326
323
  async function listDDLChangelog(opts) {
327
324
  const client = (0, http_1.getHttpClient)();
328
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/changelog", {
325
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/changelog', {
329
326
  table: opts.table,
330
327
  since: opts.since,
331
328
  until: opts.until,
@@ -337,18 +334,18 @@ async function listDDLChangelog(opts) {
337
334
  let response;
338
335
  try {
339
336
  response = await client.get(url);
340
- (0, client_1.traceHttp)("GET", url, start, response);
337
+ (0, client_1.traceHttp)('GET', url, start, response);
341
338
  }
342
339
  catch (err) {
343
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
344
- await mapDbHttpError(err, url, "Failed to list DDL changelog");
340
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
341
+ await mapDbHttpError(err, url, 'Failed to list DDL changelog');
345
342
  throw err; // 不可达
346
343
  }
347
344
  const body = (await response.json());
348
345
  const data = (0, client_1.extractData)(body);
349
346
  return {
350
347
  items: data.items ?? [],
351
- nextCursor: data.nextCursor && data.nextCursor !== "" ? data.nextCursor : null,
348
+ nextCursor: data.nextCursor && data.nextCursor !== '' ? data.nextCursor : null,
352
349
  hasMore: Boolean(data.hasMore),
353
350
  };
354
351
  }
@@ -359,7 +356,7 @@ async function listDDLChangelog(opts) {
359
356
  */
360
357
  async function getAuditStatus(opts) {
361
358
  const client = (0, http_1.getHttpClient)();
362
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/audit/status", {
359
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/audit/status', {
363
360
  table: opts.table,
364
361
  dbBranch: opts.dbBranch,
365
362
  });
@@ -367,11 +364,11 @@ async function getAuditStatus(opts) {
367
364
  let response;
368
365
  try {
369
366
  response = await client.get(url);
370
- (0, client_1.traceHttp)("GET", url, start, response);
367
+ (0, client_1.traceHttp)('GET', url, start, response);
371
368
  }
372
369
  catch (err) {
373
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
374
- await mapDbHttpError(err, url, "Failed to get audit status");
370
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
371
+ await mapDbHttpError(err, url, 'Failed to get audit status');
375
372
  throw err; // 不可达
376
373
  }
377
374
  const respBody = (await response.json());
@@ -384,30 +381,30 @@ async function getAuditStatus(opts) {
384
381
  */
385
382
  async function setAuditConfig(opts) {
386
383
  const client = (0, http_1.getHttpClient)();
387
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/audit/config");
384
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/audit/config');
388
385
  const body = {
389
386
  table: opts.table,
390
387
  enabled: opts.enabled,
391
388
  };
392
- if (opts.retention !== undefined && opts.retention !== "")
389
+ if (opts.retention !== undefined && opts.retention !== '')
393
390
  body.retention = opts.retention;
394
- if (opts.dbBranch !== undefined && opts.dbBranch !== "")
391
+ if (opts.dbBranch !== undefined && opts.dbBranch !== '')
395
392
  body.dbBranch = opts.dbBranch;
396
393
  const start = Date.now();
397
394
  let response;
398
395
  try {
399
396
  response = await client.post(url, body);
400
- (0, client_1.traceHttp)("POST", url, start, response);
397
+ (0, client_1.traceHttp)('POST', url, start, response);
401
398
  }
402
399
  catch (err) {
403
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
404
- await mapDbHttpError(err, url, "Failed to set audit config");
400
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
401
+ await mapDbHttpError(err, url, 'Failed to set audit config');
405
402
  throw err; // 不可达
406
403
  }
407
404
  const respBody = (await response.json());
408
405
  const data = (0, client_1.extractData)(respBody);
409
406
  if (!data.status) {
410
- throw new error_1.AppError("INTERNAL_DB_ERROR", "audit config response missing status field");
407
+ throw new error_1.AppError('INTERNAL_DB_ERROR', 'audit config response missing status field');
411
408
  }
412
409
  return data.status;
413
410
  }
@@ -425,11 +422,11 @@ async function setAuditConfig(opts) {
425
422
  */
426
423
  async function listAuditLog(opts) {
427
424
  if (opts.tables.length === 0) {
428
- throw new error_1.AppError("ARGS_INVALID", "at least one table is required");
425
+ throw new error_1.AppError('ARGS_INVALID', 'at least one table is required');
429
426
  }
430
427
  const client = (0, http_1.getHttpClient)();
431
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/audit/log", {
432
- tables: opts.tables.join(","),
428
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/audit/log', {
429
+ tables: opts.tables.join(','),
433
430
  since: opts.since,
434
431
  until: opts.until,
435
432
  limit: opts.limit !== undefined ? String(opts.limit) : undefined,
@@ -440,18 +437,18 @@ async function listAuditLog(opts) {
440
437
  let response;
441
438
  try {
442
439
  response = await client.get(url);
443
- (0, client_1.traceHttp)("GET", url, start, response);
440
+ (0, client_1.traceHttp)('GET', url, start, response);
444
441
  }
445
442
  catch (err) {
446
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
447
- await mapDbHttpError(err, url, "Failed to list audit log");
443
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
444
+ await mapDbHttpError(err, url, 'Failed to list audit log');
448
445
  throw err; // 不可达
449
446
  }
450
447
  const body = (await response.json());
451
448
  const data = (0, client_1.extractData)(body);
452
449
  return {
453
450
  items: data.items ?? [],
454
- nextCursor: data.nextCursor && data.nextCursor !== "" ? data.nextCursor : null,
451
+ nextCursor: data.nextCursor && data.nextCursor !== '' ? data.nextCursor : null,
455
452
  hasMore: Boolean(data.hasMore),
456
453
  skipped: data.skipped ?? [],
457
454
  };
@@ -463,7 +460,7 @@ async function listAuditLog(opts) {
463
460
  */
464
461
  async function migrationInit(opts) {
465
462
  const client = (0, http_1.getHttpClient)();
466
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/enableMultiEnv");
463
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/enableMultiEnv');
467
464
  const body = {};
468
465
  if (opts.syncData !== undefined)
469
466
  body.syncData = opts.syncData;
@@ -471,11 +468,11 @@ async function migrationInit(opts) {
471
468
  let response;
472
469
  try {
473
470
  response = await client.post(url, body);
474
- (0, client_1.traceHttp)("POST", url, start, response);
471
+ (0, client_1.traceHttp)('POST', url, start, response);
475
472
  }
476
473
  catch (err) {
477
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
478
- await mapDbHttpError(err, url, "Failed to init migration");
474
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
475
+ await mapDbHttpError(err, url, 'Failed to init migration');
479
476
  throw err; // 不可达
480
477
  }
481
478
  const respBody = (await response.json());
@@ -487,16 +484,16 @@ async function migrationInit(opts) {
487
484
  */
488
485
  async function migrate(opts) {
489
486
  const client = (0, http_1.getHttpClient)();
490
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/migration");
487
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/migration');
491
488
  const start = Date.now();
492
489
  let response;
493
490
  try {
494
491
  response = await client.post(url, { dryRun: opts.dryRun });
495
- (0, client_1.traceHttp)("POST", url, start, response);
492
+ (0, client_1.traceHttp)('POST', url, start, response);
496
493
  }
497
494
  catch (err) {
498
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
499
- await mapDbHttpError(err, url, "Failed to migrate");
495
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
496
+ await mapDbHttpError(err, url, 'Failed to migrate');
500
497
  throw err; // 不可达
501
498
  }
502
499
  const respBody = (await response.json());
@@ -509,7 +506,7 @@ async function migrate(opts) {
509
506
  */
510
507
  async function getMigrationStatus(opts) {
511
508
  const client = (0, http_1.getHttpClient)();
512
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/migration/status", {
509
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/migration/status', {
513
510
  taskId: opts.taskId,
514
511
  dbBranch: opts.dbBranch,
515
512
  });
@@ -517,11 +514,11 @@ async function getMigrationStatus(opts) {
517
514
  let response;
518
515
  try {
519
516
  response = await client.get(url);
520
- (0, client_1.traceHttp)("GET", url, start, response);
517
+ (0, client_1.traceHttp)('GET', url, start, response);
521
518
  }
522
519
  catch (err) {
523
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
524
- await mapDbHttpError(err, url, "Failed to get migration status");
520
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
521
+ await mapDbHttpError(err, url, 'Failed to get migration status');
525
522
  throw err; // 不可达
526
523
  }
527
524
  const body = (await response.json());
@@ -534,7 +531,7 @@ async function getMigrationStatus(opts) {
534
531
  */
535
532
  async function recover(opts) {
536
533
  const client = (0, http_1.getHttpClient)();
537
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/recovery");
534
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/recovery');
538
535
  const start = Date.now();
539
536
  let response;
540
537
  try {
@@ -542,11 +539,11 @@ async function recover(opts) {
542
539
  target: opts.target,
543
540
  dryRun: opts.dryRun,
544
541
  });
545
- (0, client_1.traceHttp)("POST", url, start, response);
542
+ (0, client_1.traceHttp)('POST', url, start, response);
546
543
  }
547
544
  catch (err) {
548
- (0, client_1.traceHttp)("POST", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
549
- await mapDbHttpError(err, url, "Failed to recover");
545
+ (0, client_1.traceHttp)('POST', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
546
+ await mapDbHttpError(err, url, 'Failed to recover');
550
547
  throw err; // 不可达
551
548
  }
552
549
  const respBody = (await response.json());
@@ -558,7 +555,7 @@ async function recover(opts) {
558
555
  */
559
556
  async function getRecoveryPreview(opts) {
560
557
  const client = (0, http_1.getHttpClient)();
561
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/recovery/preview", {
558
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/recovery/preview', {
562
559
  previewRequestId: opts.previewRequestId,
563
560
  dbBranch: opts.dbBranch,
564
561
  });
@@ -566,11 +563,34 @@ async function getRecoveryPreview(opts) {
566
563
  let response;
567
564
  try {
568
565
  response = await client.get(url);
569
- (0, client_1.traceHttp)("GET", url, start, response);
566
+ (0, client_1.traceHttp)('GET', url, start, response);
567
+ }
568
+ catch (err) {
569
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
570
+ await mapDbHttpError(err, url, 'Failed to get recovery preview');
571
+ throw err; // 不可达
572
+ }
573
+ const body = (await response.json());
574
+ return (0, client_1.extractData)(body);
575
+ }
576
+ /**
577
+ * 后端:GET /v1/dataloom/app/{appId}/db/recovery/status
578
+ * CLI apply 触发后定时调本接口直到 status=success/failed。dataloom 内部 Redis
579
+ * 维护 workspace 级 restore 状态,无需传 task id;workspace+dbBranch 维度同时
580
+ * 只允许一个 restore 进行中。
581
+ */
582
+ async function getRecoveryStatus(opts) {
583
+ const client = (0, http_1.getHttpClient)();
584
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/recovery/status', { dbBranch: opts.dbBranch });
585
+ const start = Date.now();
586
+ let response;
587
+ try {
588
+ response = await client.get(url);
589
+ (0, client_1.traceHttp)('GET', url, start, response);
570
590
  }
571
591
  catch (err) {
572
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
573
- await mapDbHttpError(err, url, "Failed to get recovery preview");
592
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
593
+ await mapDbHttpError(err, url, 'Failed to get recovery status');
574
594
  throw err; // 不可达
575
595
  }
576
596
  const body = (await response.json());
@@ -582,16 +602,16 @@ async function getRecoveryPreview(opts) {
582
602
  */
583
603
  async function getDbQuota(opts) {
584
604
  const client = (0, http_1.getHttpClient)();
585
- const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/quota", { dbBranch: opts.dbBranch });
605
+ const url = (0, client_1.buildInnerUrl)(opts.appId, '/db/quota', { dbBranch: opts.dbBranch });
586
606
  const start = Date.now();
587
607
  let response;
588
608
  try {
589
609
  response = await client.get(url);
590
- (0, client_1.traceHttp)("GET", url, start, response);
610
+ (0, client_1.traceHttp)('GET', url, start, response);
591
611
  }
592
612
  catch (err) {
593
- (0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
594
- await mapDbHttpError(err, url, "Failed to get db quota");
613
+ (0, client_1.traceHttp)('GET', url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
614
+ await mapDbHttpError(err, url, 'Failed to get db quota');
595
615
  throw err; // 不可达
596
616
  }
597
617
  const respBody = (await response.json());