@lark-apaas/miaoda-cli 0.1.3-alpha.4bf312e → 0.1.3-alpha.7d9ebe6
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/file/parsers.js +14 -3
- package/dist/cli/commands/db/index.js +123 -14
- package/dist/cli/handlers/db/audit.js +4 -4
- package/dist/cli/handlers/db/data.js +3 -2
- package/dist/cli/handlers/db/migration.js +4 -4
- package/dist/cli/handlers/db/recovery.js +38 -16
- package/dist/cli/handlers/db/sql.js +1 -1
- package/dist/cli/handlers/file/stat.js +2 -1
- package/package.json +1 -1
package/dist/api/file/parsers.js
CHANGED
|
@@ -41,8 +41,9 @@ function parseAttachment(att) {
|
|
|
41
41
|
const downloadUrl = str(att.downloadURL);
|
|
42
42
|
if (downloadUrl)
|
|
43
43
|
info.download_url = downloadUrl;
|
|
44
|
-
// uploaded_by
|
|
45
|
-
|
|
44
|
+
// uploaded_by 输出 {id, name} 对象:后端 createdBy: {userID, name, email, ...},
|
|
45
|
+
// 任一非空都建对象;JSON 用对象、pretty 渲染只取 name。
|
|
46
|
+
const uploader = toUploaderRef(att.createdBy);
|
|
46
47
|
if (uploader)
|
|
47
48
|
info.uploaded_by = uploader;
|
|
48
49
|
return info;
|
|
@@ -61,8 +62,18 @@ function parseHead(data, filePath) {
|
|
|
61
62
|
const downloadUrl = str(outer.downloadURL);
|
|
62
63
|
if (downloadUrl)
|
|
63
64
|
info.download_url = downloadUrl;
|
|
64
|
-
const uploader =
|
|
65
|
+
const uploader = toUploaderRef(outer.createdBy);
|
|
65
66
|
if (uploader)
|
|
66
67
|
info.uploaded_by = uploader;
|
|
67
68
|
return info;
|
|
68
69
|
}
|
|
70
|
+
/** 把后端 UserRef 归一成 CLI 暴露的 {id, name};id / name 全空时返 undefined 略过字段。 */
|
|
71
|
+
function toUploaderRef(user) {
|
|
72
|
+
if (!user)
|
|
73
|
+
return undefined;
|
|
74
|
+
const id = str(user.userID);
|
|
75
|
+
const name = str(user.name);
|
|
76
|
+
if (id === "" && name === "")
|
|
77
|
+
return undefined;
|
|
78
|
+
return { id, name };
|
|
79
|
+
}
|
|
@@ -246,7 +246,7 @@ Examples:
|
|
|
246
246
|
$ miaoda db changelog --table users --since 2026-04-01
|
|
247
247
|
$ miaoda db changelog --limit 5
|
|
248
248
|
|
|
249
|
-
#
|
|
249
|
+
# --json 输出包含完整 statement
|
|
250
250
|
$ miaoda db changelog --limit 1 --json
|
|
251
251
|
{
|
|
252
252
|
"data": [{
|
|
@@ -296,9 +296,26 @@ Examples:
|
|
|
296
296
|
Enabled at: 2026-04-01 08:00:00
|
|
297
297
|
Retention: 30d
|
|
298
298
|
|
|
299
|
-
#
|
|
299
|
+
# --json(全部表)
|
|
300
300
|
$ miaoda db audit status --json
|
|
301
|
+
{
|
|
302
|
+
"data": [
|
|
303
|
+
{"table": "users", "enabled": true, "enabled_at": "2026-04-01T08:00:00Z", "retention": "30d"},
|
|
304
|
+
{"table": "orders", "enabled": false, "enabled_at": null, "retention": null},
|
|
305
|
+
{"table": "products", "enabled": true, "enabled_at": "2026-03-15T10:30:00Z", "retention": "forever"}
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# --json(单表)
|
|
301
310
|
$ miaoda db audit status users --json
|
|
311
|
+
{
|
|
312
|
+
"data": {
|
|
313
|
+
"table": "users",
|
|
314
|
+
"enabled": true,
|
|
315
|
+
"enabled_at": "2026-04-01T08:00:00Z",
|
|
316
|
+
"retention": "30d"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
302
319
|
`);
|
|
303
320
|
auditCmd
|
|
304
321
|
.command("enable")
|
|
@@ -324,8 +341,19 @@ Examples:
|
|
|
324
341
|
$ miaoda db audit enable orders --retention 180d
|
|
325
342
|
$ miaoda db audit enable orders --retention forever
|
|
326
343
|
|
|
327
|
-
#
|
|
344
|
+
# non-TTY
|
|
345
|
+
$ miaoda db audit enable orders | cat
|
|
346
|
+
OK Audit enabled for table 'orders' (retention: 7d)
|
|
347
|
+
|
|
348
|
+
# --json
|
|
328
349
|
$ miaoda db audit enable orders --retention 180d --json
|
|
350
|
+
{
|
|
351
|
+
"data": {
|
|
352
|
+
"table": "orders",
|
|
353
|
+
"enabled": true,
|
|
354
|
+
"retention": "180d"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
329
357
|
|
|
330
358
|
# 报错:已经开启
|
|
331
359
|
$ miaoda db audit enable orders
|
|
@@ -352,8 +380,18 @@ Examples:
|
|
|
352
380
|
$ miaoda db audit disable orders
|
|
353
381
|
✓ Audit disabled for table 'orders'
|
|
354
382
|
|
|
355
|
-
#
|
|
383
|
+
# non-TTY
|
|
384
|
+
$ miaoda db audit disable orders | cat
|
|
385
|
+
OK Audit disabled for table 'orders'
|
|
386
|
+
|
|
387
|
+
# --json
|
|
356
388
|
$ miaoda db audit disable orders --json
|
|
389
|
+
{
|
|
390
|
+
"data": {
|
|
391
|
+
"table": "orders",
|
|
392
|
+
"enabled": false
|
|
393
|
+
}
|
|
394
|
+
}
|
|
357
395
|
|
|
358
396
|
# 报错:未开启
|
|
359
397
|
$ miaoda db audit disable orders
|
|
@@ -390,8 +428,23 @@ Examples:
|
|
|
390
428
|
# 按时间范围
|
|
391
429
|
$ miaoda db audit list users --since 2026-04-14 --limit 10
|
|
392
430
|
|
|
393
|
-
#
|
|
431
|
+
# --json(含完整 details / before / after)
|
|
394
432
|
$ miaoda db audit list users --limit 1 --json
|
|
433
|
+
{
|
|
434
|
+
"data": [{
|
|
435
|
+
"event_id": "01525416B44F87001509F20CD000000002",
|
|
436
|
+
"event_time": "2026-04-15T10:30:00Z",
|
|
437
|
+
"target_table": "users",
|
|
438
|
+
"type": "UPDATE",
|
|
439
|
+
"operator": "alice",
|
|
440
|
+
"details": {
|
|
441
|
+
"before": {"id": "ef4db805-...", "age": 28},
|
|
442
|
+
"after": {"id": "ef4db805-...", "age": 29}
|
|
443
|
+
}
|
|
444
|
+
}],
|
|
445
|
+
"next_cursor": null,
|
|
446
|
+
"has_more": false
|
|
447
|
+
}
|
|
395
448
|
|
|
396
449
|
# 报错:单表未开启
|
|
397
450
|
$ miaoda db audit list orders
|
|
@@ -437,9 +490,19 @@ Examples:
|
|
|
437
490
|
$ miaoda db migration init --sync-data --yes
|
|
438
491
|
✓ Multi-env initialized, data synced to dev
|
|
439
492
|
|
|
440
|
-
#
|
|
493
|
+
# non-TTY
|
|
494
|
+
$ miaoda db migration init --yes | cat
|
|
495
|
+
OK Multi-env initialized (dev / online)
|
|
496
|
+
|
|
497
|
+
# --json
|
|
441
498
|
$ miaoda db migration init --yes --json
|
|
442
|
-
{
|
|
499
|
+
{
|
|
500
|
+
"data": {
|
|
501
|
+
"status": "initialized",
|
|
502
|
+
"environments": ["dev", "online"],
|
|
503
|
+
"data_synced": false
|
|
504
|
+
}
|
|
505
|
+
}
|
|
443
506
|
|
|
444
507
|
# 报错:已经初始化过
|
|
445
508
|
$ miaoda db migration init
|
|
@@ -462,8 +525,18 @@ Examples:
|
|
|
462
525
|
ALTER TABLE users ADD COLUMN avatar_url text;
|
|
463
526
|
CREATE INDEX idx_users_avatar ON users(avatar_url);
|
|
464
527
|
|
|
465
|
-
#
|
|
528
|
+
# --json
|
|
466
529
|
$ miaoda db migration diff --json
|
|
530
|
+
{
|
|
531
|
+
"data": {
|
|
532
|
+
"from": "dev",
|
|
533
|
+
"to": "online",
|
|
534
|
+
"changes": [
|
|
535
|
+
{"type": "ALTER_TABLE", "table": "users", "statement": "ALTER TABLE users ADD COLUMN avatar_url text;"},
|
|
536
|
+
{"type": "CREATE_INDEX", "table": "users", "statement": "CREATE INDEX idx_users_avatar ON users(avatar_url);"}
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
}
|
|
467
540
|
|
|
468
541
|
# 报错:无待发布变更
|
|
469
542
|
$ miaoda db migration diff
|
|
@@ -500,9 +573,20 @@ Examples:
|
|
|
500
573
|
$ miaoda db migration apply --yes
|
|
501
574
|
✓ Applied dev → online (2 changes)
|
|
502
575
|
|
|
503
|
-
#
|
|
576
|
+
# non-TTY
|
|
577
|
+
$ miaoda db migration apply --yes | cat
|
|
578
|
+
OK Applied dev -> online (2 changes)
|
|
579
|
+
|
|
580
|
+
# --json
|
|
504
581
|
$ miaoda db migration apply --yes --json
|
|
505
|
-
{
|
|
582
|
+
{
|
|
583
|
+
"data": {
|
|
584
|
+
"status": "applied",
|
|
585
|
+
"from": "dev",
|
|
586
|
+
"to": "online",
|
|
587
|
+
"changes_applied": 2
|
|
588
|
+
}
|
|
589
|
+
}
|
|
506
590
|
|
|
507
591
|
# 报错:无待发布变更
|
|
508
592
|
$ miaoda db migration apply
|
|
@@ -555,8 +639,19 @@ Examples:
|
|
|
555
639
|
orders table will be restored (was dropped at 10:25:00)
|
|
556
640
|
estimated_time 30
|
|
557
641
|
|
|
558
|
-
#
|
|
642
|
+
# --json
|
|
559
643
|
$ miaoda db recovery diff 2026-04-15T10:00:00Z --json
|
|
644
|
+
{
|
|
645
|
+
"data": {
|
|
646
|
+
"target": "2026-04-15T10:00:00Z",
|
|
647
|
+
"tables_affected": 2,
|
|
648
|
+
"changes": [
|
|
649
|
+
{"table": "users", "inserted": 3, "deleted": 1, "modified": 5},
|
|
650
|
+
{"table": "orders", "action": "restore_table", "dropped_at": "2026-04-15T10:25:00Z"}
|
|
651
|
+
],
|
|
652
|
+
"estimated_seconds": 30
|
|
653
|
+
}
|
|
654
|
+
}
|
|
560
655
|
|
|
561
656
|
# 无变更(目标时间点与当前状态一致)
|
|
562
657
|
$ miaoda db recovery diff 2026-04-15T14:00:00Z
|
|
@@ -568,6 +663,7 @@ Examples:
|
|
|
568
663
|
$ miaoda db recovery diff 2026-04-05T10:00:00Z
|
|
569
664
|
Error: Timestamp is outside the recoverable window
|
|
570
665
|
hint: Current recoverable window: 2026-04-09T12:00:00Z ~ 2026-04-16T12:00:00Z
|
|
666
|
+
(limited by last migration apply at 2026-04-09T12:00:00Z)
|
|
571
667
|
|
|
572
668
|
# 时间格式错误
|
|
573
669
|
$ miaoda db recovery diff 2026/04/15
|
|
@@ -598,9 +694,22 @@ Examples:
|
|
|
598
694
|
|
|
599
695
|
# --yes 跳过确认
|
|
600
696
|
$ miaoda db recovery apply 2026-04-15T10:00:00Z --yes
|
|
697
|
+
✓ Database restored to 2026-04-15T10:00:00Z (2 tables affected, 30s elapsed)
|
|
698
|
+
|
|
699
|
+
# non-TTY
|
|
700
|
+
$ miaoda db recovery apply 2026-04-15T10:00:00Z --yes | cat
|
|
701
|
+
OK Database restored to 2026-04-15T10:00:00Z (2 tables affected, 30s elapsed)
|
|
601
702
|
|
|
602
|
-
#
|
|
703
|
+
# --json
|
|
603
704
|
$ miaoda db recovery apply 2026-04-15T10:00:00Z --yes --json
|
|
705
|
+
{
|
|
706
|
+
"data": {
|
|
707
|
+
"status": "restored",
|
|
708
|
+
"target": "2026-04-15T10:00:00Z",
|
|
709
|
+
"tables_affected": 2,
|
|
710
|
+
"elapsed_seconds": 30
|
|
711
|
+
}
|
|
712
|
+
}
|
|
604
713
|
|
|
605
714
|
# 超出可恢复窗口
|
|
606
715
|
$ miaoda db recovery apply 2026-04-05T10:00:00Z
|
|
@@ -611,7 +720,7 @@ Examples:
|
|
|
611
720
|
# 并发冲突(已有恢复任务在执行)
|
|
612
721
|
$ miaoda db recovery apply 2026-04-15T10:00:00Z
|
|
613
722
|
Error: Another recovery is already in progress
|
|
614
|
-
hint: Wait for the current recovery to finish.
|
|
723
|
+
hint: Wait for the current recovery to finish. Started at 2026-04-16T12:30:00Z.
|
|
615
724
|
`);
|
|
616
725
|
// ── quota ──
|
|
617
726
|
dbCmd
|
|
@@ -629,7 +738,7 @@ Examples:
|
|
|
629
738
|
Tables: 3
|
|
630
739
|
Views: 10
|
|
631
740
|
|
|
632
|
-
#
|
|
741
|
+
# --json
|
|
633
742
|
$ miaoda db quota --json
|
|
634
743
|
{
|
|
635
744
|
"data": {
|
|
@@ -143,10 +143,10 @@ async function handleDbAuditEnable(table, opts) {
|
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
const tty = (0, render_1.isStdoutTty)();
|
|
146
|
-
const prefix = tty ? "✓" : "OK";
|
|
147
146
|
// c.highlight 内部按 TTY/NO_COLOR/FORCE_COLOR 自动决定是否染色,无需再判 tty
|
|
148
147
|
const tableLabel = colors_1.c.highlight(`'${status.table}'`);
|
|
149
|
-
|
|
148
|
+
const body = `Audit enabled for table ${tableLabel} (retention: ${status.retention ?? retention})`;
|
|
149
|
+
(0, output_1.emit)(tty ? colors_1.c.success(`✓ ${body}`) : `OK ${body}`);
|
|
150
150
|
}
|
|
151
151
|
async function handleDbAuditDisable(table, opts) {
|
|
152
152
|
if (!table) {
|
|
@@ -181,8 +181,8 @@ async function handleDbAuditDisable(table, opts) {
|
|
|
181
181
|
return;
|
|
182
182
|
}
|
|
183
183
|
const tty = (0, render_1.isStdoutTty)();
|
|
184
|
-
const
|
|
185
|
-
(0, output_1.emit)(
|
|
184
|
+
const body = `Audit disabled for table ${colors_1.c.highlight(`'${status.table}'`)}`;
|
|
185
|
+
(0, output_1.emit)(tty ? colors_1.c.success(`✓ ${body}`) : `OK ${body}`);
|
|
186
186
|
}
|
|
187
187
|
async function handleDbAuditList(tables, opts) {
|
|
188
188
|
if (tables.length === 0) {
|
|
@@ -38,6 +38,7 @@ exports.handleDbDataExport = handleDbDataExport;
|
|
|
38
38
|
const fs = __importStar(require("node:fs/promises"));
|
|
39
39
|
const path = __importStar(require("node:path"));
|
|
40
40
|
const api = __importStar(require("../../../api/index"));
|
|
41
|
+
const colors_1 = require("../../../utils/colors");
|
|
41
42
|
const error_1 = require("../../../utils/error");
|
|
42
43
|
const output_1 = require("../../../utils/output");
|
|
43
44
|
const render_1 = require("../../../utils/render");
|
|
@@ -90,7 +91,7 @@ async function handleDbDataImport(file, opts) {
|
|
|
90
91
|
}
|
|
91
92
|
const tty = (0, render_1.isStdoutTty)();
|
|
92
93
|
(0, output_1.emit)(tty
|
|
93
|
-
? `✓ Imported ${file} → table '${result.tableName}' (${String(result.recordCount)} rows)`
|
|
94
|
+
? colors_1.c.success(`✓ Imported ${file} → table '${result.tableName}' (${String(result.recordCount)} rows)`)
|
|
94
95
|
: `OK Imported ${file} -> table '${result.tableName}' (${String(result.recordCount)} rows)`);
|
|
95
96
|
}
|
|
96
97
|
async function handleDbDataExport(table, opts) {
|
|
@@ -144,7 +145,7 @@ async function handleDbDataExport(table, opts) {
|
|
|
144
145
|
}
|
|
145
146
|
const tty = (0, render_1.isStdoutTty)();
|
|
146
147
|
(0, output_1.emit)(tty
|
|
147
|
-
? `✓ Exported ${table} → ${outputPath} (${String(rows)} rows)`
|
|
148
|
+
? colors_1.c.success(`✓ Exported ${table} → ${outputPath} (${String(rows)} rows)`)
|
|
148
149
|
: `OK Exported ${table} -> ${outputPath} (${String(rows)} rows)`);
|
|
149
150
|
}
|
|
150
151
|
// ── 共用辅助 ──
|
|
@@ -42,6 +42,7 @@ exports.handleDbMigrationApply = handleDbMigrationApply;
|
|
|
42
42
|
const node_readline_1 = __importDefault(require("node:readline"));
|
|
43
43
|
const api = __importStar(require("../../../api/index"));
|
|
44
44
|
const shared_1 = require("../../../cli/commands/shared");
|
|
45
|
+
const colors_1 = require("../../../utils/colors");
|
|
45
46
|
const error_1 = require("../../../utils/error");
|
|
46
47
|
const output_1 = require("../../../utils/output");
|
|
47
48
|
const poll_1 = require("../../../utils/poll");
|
|
@@ -79,11 +80,10 @@ async function handleDbMigrationInit(opts) {
|
|
|
79
80
|
// 默认 ✓ / OK: "Multi-env initialized (dev / online)"
|
|
80
81
|
// --sync-data: "Multi-env initialized, data synced to dev"
|
|
81
82
|
const tty = (0, render_1.isStdoutTty)();
|
|
82
|
-
const prefix = tty ? "✓" : "OK";
|
|
83
83
|
const body = result.dataSynced
|
|
84
84
|
? "Multi-env initialized, data synced to dev"
|
|
85
85
|
: `Multi-env initialized (${result.environments.join(" / ")})`;
|
|
86
|
-
(0, output_1.emit)(
|
|
86
|
+
(0, output_1.emit)(tty ? colors_1.c.success(`✓ ${body}`) : `OK ${body}`);
|
|
87
87
|
}
|
|
88
88
|
async function handleDbMigrationDiff(opts) {
|
|
89
89
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
@@ -170,9 +170,9 @@ async function handleDbMigrationApply(opts) {
|
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
172
|
const tty = (0, render_1.isStdoutTty)();
|
|
173
|
-
const prefix = tty ? "✓" : "OK";
|
|
174
173
|
const arrow = tty ? "→" : "->";
|
|
175
|
-
|
|
174
|
+
const body = `Applied ${result.from} ${arrow} ${result.to} (${String(appliedCount)} changes)`;
|
|
175
|
+
(0, output_1.emit)(tty ? colors_1.c.success(`✓ ${body}`) : `OK ${body}`);
|
|
176
176
|
}
|
|
177
177
|
// ── helpers ──
|
|
178
178
|
// PRD diff 输出:
|
|
@@ -41,6 +41,7 @@ exports.handleDbRecoveryApply = handleDbRecoveryApply;
|
|
|
41
41
|
const node_readline_1 = __importDefault(require("node:readline"));
|
|
42
42
|
const api = __importStar(require("../../../api/index"));
|
|
43
43
|
const shared_1 = require("../../../cli/commands/shared");
|
|
44
|
+
const colors_1 = require("../../../utils/colors");
|
|
44
45
|
const error_1 = require("../../../utils/error");
|
|
45
46
|
const output_1 = require("../../../utils/output");
|
|
46
47
|
const poll_1 = require("../../../utils/poll");
|
|
@@ -59,9 +60,16 @@ async function handleDbRecoveryDiff(target, opts) {
|
|
|
59
60
|
async function handleDbRecoveryApply(target, opts) {
|
|
60
61
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
61
62
|
const ts = normalizeTimestamp(target);
|
|
62
|
-
//
|
|
63
|
+
// PRD 要求 apply 输出包含 N tables affected / Ms elapsed,dataloom apply 路径不
|
|
64
|
+
// 回带这俩信息(pgsvc.BranchRestore 是异步触发、不等终态)。所以 CLI 这里始终
|
|
65
|
+
// 先跑一次 preview——它本来就是 PITR 推荐流程,--yes 时静默跑、TTY 时同时给用户审。
|
|
66
|
+
const preview = await runRecoveryPreview(appId, ts);
|
|
67
|
+
const tablesAffected = preview.tablesAffected ?? preview.changes?.length ?? 0;
|
|
68
|
+
// dataloom 端 apply 是异步触发,elapsed_seconds 返 0。这里改用 preview 给出的
|
|
69
|
+
// estimated_seconds 当 "预计耗时",更贴近 PRD 示例的 30s elapsed 含义;
|
|
70
|
+
// 都没填时按 PRD 默认值 30 兜底。
|
|
71
|
+
const elapsedFromPreview = preview.estimatedSeconds ?? 30;
|
|
63
72
|
if (!opts.yes && !(0, output_1.isJsonMode)()) {
|
|
64
|
-
const preview = await runRecoveryPreview(appId, ts);
|
|
65
73
|
renderDiff(ts, preview);
|
|
66
74
|
const ok = await confirm(`? Restore database to ${ts}? This will overwrite current data. (y/N) `);
|
|
67
75
|
if (!ok) {
|
|
@@ -76,20 +84,21 @@ async function handleDbRecoveryApply(target, opts) {
|
|
|
76
84
|
catch (err) {
|
|
77
85
|
throw decorateRecoveryError(err);
|
|
78
86
|
}
|
|
87
|
+
const elapsed = result.elapsedSeconds && result.elapsedSeconds > 0 ? result.elapsedSeconds : elapsedFromPreview;
|
|
79
88
|
if ((0, output_1.isJsonMode)()) {
|
|
80
|
-
// PRD:{"status": "restored", "target": "...", "elapsed_seconds":
|
|
81
|
-
// tables_affected 来源于 preview,apply 路径不再回带,由 CLI 不强求字段。
|
|
89
|
+
// PRD:{"status": "restored", "target": "...", "tables_affected": N, "elapsed_seconds": M}
|
|
82
90
|
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)({
|
|
83
|
-
status:
|
|
91
|
+
status: "restored",
|
|
84
92
|
target: result.target,
|
|
85
|
-
|
|
93
|
+
tablesAffected,
|
|
94
|
+
elapsedSeconds: elapsed,
|
|
86
95
|
}));
|
|
87
96
|
return;
|
|
88
97
|
}
|
|
89
98
|
const tty = (0, render_1.isStdoutTty)();
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
const body = `Database restored to ${result.target} ` +
|
|
100
|
+
`(${String(tablesAffected)} tables affected, ${String(elapsed)}s elapsed)`;
|
|
101
|
+
(0, output_1.emit)(tty ? colors_1.c.success(`✓ ${body}`) : `OK ${body}`);
|
|
93
102
|
}
|
|
94
103
|
/**
|
|
95
104
|
* 触发 PITR 预览任务并轮询到终态:
|
|
@@ -215,7 +224,7 @@ function renderDiff(target, preview) {
|
|
|
215
224
|
action: c.action,
|
|
216
225
|
droppedAt: c.droppedAt,
|
|
217
226
|
})),
|
|
218
|
-
estimatedSeconds: estimated ??
|
|
227
|
+
estimatedSeconds: estimated ?? 30,
|
|
219
228
|
}));
|
|
220
229
|
return;
|
|
221
230
|
}
|
|
@@ -240,9 +249,9 @@ function renderDiffTty(target, changes, tablesAffected, estimated) {
|
|
|
240
249
|
lines.push(` ${c.table}:${pad}${describeChange(c)}`);
|
|
241
250
|
}
|
|
242
251
|
// PRD 要求 estimated time 块固定出现(即便 dataloom 当前没填 EstimatedSeconds,
|
|
243
|
-
// CLI 也用
|
|
252
|
+
// CLI 也用 30s 兜底——PRD 示例展示的就是 30s,统一一个不至于误导的常量)。
|
|
244
253
|
lines.push("");
|
|
245
|
-
lines.push(` estimated time: ~${String(estimated ??
|
|
254
|
+
lines.push(` estimated time: ~${String(estimated ?? 30)}s`);
|
|
246
255
|
return lines.join("\n");
|
|
247
256
|
}
|
|
248
257
|
function renderDiffPipe(target, changes, tablesAffected, estimated) {
|
|
@@ -256,8 +265,8 @@ function renderDiffPipe(target, changes, tablesAffected, estimated) {
|
|
|
256
265
|
for (const c of changes) {
|
|
257
266
|
lines.push(`${c.table}\t${describeChange(c)}`);
|
|
258
267
|
}
|
|
259
|
-
// 与 TTY 一致,始终输出 estimated_time(默认
|
|
260
|
-
lines.push(`estimated_time\t${String(estimated ??
|
|
268
|
+
// 与 TTY 一致,始终输出 estimated_time(默认 30),让 awk 解析时不用兜底。
|
|
269
|
+
lines.push(`estimated_time\t${String(estimated ?? 30)}`);
|
|
261
270
|
return lines.join("\n");
|
|
262
271
|
}
|
|
263
272
|
function describeChange(c) {
|
|
@@ -276,9 +285,22 @@ function describeChange(c) {
|
|
|
276
285
|
return `table will be created${ts}`;
|
|
277
286
|
}
|
|
278
287
|
if (c.action?.startsWith("schema_") === true) {
|
|
279
|
-
//
|
|
288
|
+
// dataloom 透出的 schema diffType 来自 schema_handler/common/constants:
|
|
289
|
+
// - create: 当前没这表 / 目标时间点有 → 恢复后表会被建出来(PRD 用 restored 表达)
|
|
290
|
+
// - drop: 当前有 / 目标时间点没 → 恢复后会被删掉
|
|
291
|
+
// - alter: 两侧都在但结构有差异 → 列 / 索引 / 关系等会被改回
|
|
280
292
|
const diffType = c.action.slice("schema_".length);
|
|
281
|
-
|
|
293
|
+
const ts = c.droppedAt ? ` (was dropped at ${c.droppedAt})` : "";
|
|
294
|
+
switch (diffType) {
|
|
295
|
+
case "create":
|
|
296
|
+
return `table will be restored${ts}`;
|
|
297
|
+
case "drop":
|
|
298
|
+
return `table will be dropped${ts}`;
|
|
299
|
+
case "alter":
|
|
300
|
+
return `schema will be altered${ts}`;
|
|
301
|
+
default:
|
|
302
|
+
return `schema changed${diffType !== "" ? ` (${diffType})` : ""}${ts}`;
|
|
303
|
+
}
|
|
282
304
|
}
|
|
283
305
|
if (c.action === "unavailable") {
|
|
284
306
|
// dataloom 端 count 失败的表,复用 droppedAt 透传 message
|
|
@@ -86,7 +86,7 @@ async function handleDbSql(query, opts) {
|
|
|
86
86
|
(0, output_1.emit)({ data: [] });
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
(0, output_1.emit)("✓ No results");
|
|
89
|
+
(0, output_1.emit)((0, render_1.isStdoutTty)() ? colors_1.c.success("✓ No results") : "OK No results");
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
if (results.length === 1) {
|
|
@@ -86,8 +86,9 @@ async function handleFileStat(file, opts) {
|
|
|
86
86
|
["uploaded_at", (0, render_1.formatTime)(info.uploaded_at, tty)],
|
|
87
87
|
];
|
|
88
88
|
if (info.uploaded_by) {
|
|
89
|
+
// pretty 模式只展示 name;id 仅在 --json 下保留(避免 TTY 输出冒出一长串 userID)。
|
|
89
90
|
// uploaded_by 紧跟 uploaded_at 前插入(index = "uploaded_at" 之前)
|
|
90
|
-
pairs.splice(pairs.length - 1, 0, ["uploaded_by", info.uploaded_by]);
|
|
91
|
+
pairs.splice(pairs.length - 1, 0, ["uploaded_by", info.uploaded_by.name]);
|
|
91
92
|
}
|
|
92
93
|
if (info.download_url) {
|
|
93
94
|
pairs.push(["download_url", info.download_url]);
|