@lark-apaas/miaoda-cli 0.1.2-alpha.746290f → 0.1.2-alpha.87ead47
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 +73 -4
- package/dist/api/db/index.js +3 -1
- package/dist/cli/handlers/db/audit.js +61 -112
- package/dist/cli/handlers/db/quota.js +11 -1
- package/dist/cli/handlers/file/quota.js +10 -1
- package/package.json +1 -1
package/dist/api/db/api.js
CHANGED
|
@@ -5,7 +5,9 @@ exports.getSchema = getSchema;
|
|
|
5
5
|
exports.importData = importData;
|
|
6
6
|
exports.exportData = exportData;
|
|
7
7
|
exports.listDDLChangelog = listDDLChangelog;
|
|
8
|
+
exports.getAuditStatus = getAuditStatus;
|
|
8
9
|
exports.setAuditConfig = setAuditConfig;
|
|
10
|
+
exports.listAuditLog = listAuditLog;
|
|
9
11
|
exports.migrationInit = migrationInit;
|
|
10
12
|
exports.migrate = migrate;
|
|
11
13
|
exports.recover = recover;
|
|
@@ -332,13 +334,35 @@ async function listDDLChangelog(opts) {
|
|
|
332
334
|
hasMore: Boolean(data.hasMore),
|
|
333
335
|
};
|
|
334
336
|
}
|
|
335
|
-
// ── db audit → InnerAdminSetAuditConfig ──
|
|
337
|
+
// ── db audit → InnerAdminGetAuditStatus / InnerAdminSetAuditConfig ──
|
|
338
|
+
/**
|
|
339
|
+
* 后端:GET /v1/dataloom/app/{appId}/db/audit/status?table=&dbBranch=
|
|
340
|
+
* 查表审计开关状态。table 非空 → 单表过滤;空 → 返当前 workspace 全部已配置表。
|
|
341
|
+
*/
|
|
342
|
+
async function getAuditStatus(opts) {
|
|
343
|
+
const client = (0, http_1.getHttpClient)();
|
|
344
|
+
const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/audit/status", {
|
|
345
|
+
table: opts.table,
|
|
346
|
+
dbBranch: opts.dbBranch,
|
|
347
|
+
});
|
|
348
|
+
const start = Date.now();
|
|
349
|
+
let response;
|
|
350
|
+
try {
|
|
351
|
+
response = await client.get(url);
|
|
352
|
+
(0, client_1.traceHttp)("GET", url, start, response);
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
(0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
|
|
356
|
+
await mapDbHttpError(err, url, "Failed to get audit status");
|
|
357
|
+
throw err; // 不可达
|
|
358
|
+
}
|
|
359
|
+
const respBody = (await response.json());
|
|
360
|
+
const data = (0, client_1.extractData)(respBody);
|
|
361
|
+
return data.items ?? [];
|
|
362
|
+
}
|
|
336
363
|
/**
|
|
337
364
|
* 后端:POST /v1/dataloom/app/{appId}/db/audit/config
|
|
338
365
|
* 写:enabled=true 开启 / false 关闭。retention 仅 enabled=true 生效。
|
|
339
|
-
*
|
|
340
|
-
* 读路径(status / log)不在此模块——CLI handler 走 execSql 直接 SELECT
|
|
341
|
-
* `_dl_audit_config` / `_dl_audit_log` 元数据表。
|
|
342
366
|
*/
|
|
343
367
|
async function setAuditConfig(opts) {
|
|
344
368
|
const client = (0, http_1.getHttpClient)();
|
|
@@ -369,6 +393,51 @@ async function setAuditConfig(opts) {
|
|
|
369
393
|
}
|
|
370
394
|
return data.status;
|
|
371
395
|
}
|
|
396
|
+
// ── db audit log → InnerAdminListAuditLog ──
|
|
397
|
+
/**
|
|
398
|
+
* 后端:GET /v1/dataloom/app/{appId}/db/audit/log?tables=&since=&until=&limit=&cursor=&dbBranch=
|
|
399
|
+
*
|
|
400
|
+
* 走 admin-inner 接口而不是 InnerAdminExecuteSQL 直接 SELECT pg_audit:
|
|
401
|
+
* - operator 在 details JSONB 内是 user_id,服务端解析成 username
|
|
402
|
+
* - summary 后端按 type + before/after diff 合成(pg_audit 表无此列)
|
|
403
|
+
* - before/after JSONB 后端 JSON.stringify 后透传字符串,CLI 按需 parse
|
|
404
|
+
*
|
|
405
|
+
* 多表用逗号拼接走 query;后端按 target_table IN (...) 一次查。skipped 字段返
|
|
406
|
+
* 多表中无记录的表名,便于 CLI 展示 hint。
|
|
407
|
+
*/
|
|
408
|
+
async function listAuditLog(opts) {
|
|
409
|
+
if (opts.tables.length === 0) {
|
|
410
|
+
throw new error_1.AppError("ARGS_INVALID", "at least one table is required");
|
|
411
|
+
}
|
|
412
|
+
const client = (0, http_1.getHttpClient)();
|
|
413
|
+
const url = (0, client_1.buildInnerUrl)(opts.appId, "/db/audit/log", {
|
|
414
|
+
tables: opts.tables.join(","),
|
|
415
|
+
since: opts.since,
|
|
416
|
+
until: opts.until,
|
|
417
|
+
limit: opts.limit !== undefined ? String(opts.limit) : undefined,
|
|
418
|
+
cursor: opts.cursor,
|
|
419
|
+
dbBranch: opts.dbBranch,
|
|
420
|
+
});
|
|
421
|
+
const start = Date.now();
|
|
422
|
+
let response;
|
|
423
|
+
try {
|
|
424
|
+
response = await client.get(url);
|
|
425
|
+
(0, client_1.traceHttp)("GET", url, start, response);
|
|
426
|
+
}
|
|
427
|
+
catch (err) {
|
|
428
|
+
(0, client_1.traceHttp)("GET", url, start, err instanceof http_client_1.HttpError ? err.response : undefined, err);
|
|
429
|
+
await mapDbHttpError(err, url, "Failed to list audit log");
|
|
430
|
+
throw err; // 不可达
|
|
431
|
+
}
|
|
432
|
+
const body = (await response.json());
|
|
433
|
+
const data = (0, client_1.extractData)(body);
|
|
434
|
+
return {
|
|
435
|
+
items: data.items ?? [],
|
|
436
|
+
nextCursor: data.nextCursor && data.nextCursor !== "" ? data.nextCursor : null,
|
|
437
|
+
hasMore: Boolean(data.hasMore),
|
|
438
|
+
skipped: data.skipped ?? [],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
372
441
|
// ── db migration → InnerAdminMigrationInit / InnerAdminMigrate ──
|
|
373
442
|
/**
|
|
374
443
|
* 后端:POST /v1/dataloom/app/{appId}/db/enableMultiEnv
|
package/dist/api/db/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pickTableDetail = exports.flattenSchemaList = exports.parseSqlResult = exports.SQLSTATE_MAP = exports.ensureInnerSuccess = exports.buildInnerUrl = exports.getDbQuota = exports.recover = exports.migrate = exports.migrationInit = exports.setAuditConfig = exports.listDDLChangelog = exports.exportData = exports.importData = exports.getSchema = exports.execSql = void 0;
|
|
3
|
+
exports.pickTableDetail = exports.flattenSchemaList = exports.parseSqlResult = exports.SQLSTATE_MAP = exports.ensureInnerSuccess = exports.buildInnerUrl = exports.getDbQuota = exports.recover = exports.migrate = exports.migrationInit = exports.listAuditLog = exports.setAuditConfig = exports.getAuditStatus = exports.listDDLChangelog = exports.exportData = exports.importData = exports.getSchema = exports.execSql = void 0;
|
|
4
4
|
var api_1 = require("./api");
|
|
5
5
|
Object.defineProperty(exports, "execSql", { enumerable: true, get: function () { return api_1.execSql; } });
|
|
6
6
|
Object.defineProperty(exports, "getSchema", { enumerable: true, get: function () { return api_1.getSchema; } });
|
|
7
7
|
Object.defineProperty(exports, "importData", { enumerable: true, get: function () { return api_1.importData; } });
|
|
8
8
|
Object.defineProperty(exports, "exportData", { enumerable: true, get: function () { return api_1.exportData; } });
|
|
9
9
|
Object.defineProperty(exports, "listDDLChangelog", { enumerable: true, get: function () { return api_1.listDDLChangelog; } });
|
|
10
|
+
Object.defineProperty(exports, "getAuditStatus", { enumerable: true, get: function () { return api_1.getAuditStatus; } });
|
|
10
11
|
Object.defineProperty(exports, "setAuditConfig", { enumerable: true, get: function () { return api_1.setAuditConfig; } });
|
|
12
|
+
Object.defineProperty(exports, "listAuditLog", { enumerable: true, get: function () { return api_1.listAuditLog; } });
|
|
11
13
|
Object.defineProperty(exports, "migrationInit", { enumerable: true, get: function () { return api_1.migrationInit; } });
|
|
12
14
|
Object.defineProperty(exports, "migrate", { enumerable: true, get: function () { return api_1.migrate; } });
|
|
13
15
|
Object.defineProperty(exports, "recover", { enumerable: true, get: function () { return api_1.recover; } });
|
|
@@ -40,41 +40,37 @@ exports.handleDbAuditList = handleDbAuditList;
|
|
|
40
40
|
const api = __importStar(require("../../../api/index"));
|
|
41
41
|
const index_1 = require("../../../api/file/index");
|
|
42
42
|
const shared_1 = require("../../../cli/commands/shared");
|
|
43
|
+
const colors_1 = require("../../../utils/colors");
|
|
43
44
|
const error_1 = require("../../../utils/error");
|
|
44
45
|
const output_1 = require("../../../utils/output");
|
|
45
46
|
const render_1 = require("../../../utils/render");
|
|
46
|
-
const index_2 = require("../../../api/db/index");
|
|
47
47
|
const VALID_RETENTION = new Set(["7d", "30d", "180d", "360d", "forever"]);
|
|
48
48
|
async function handleDbAuditStatus(table, opts) {
|
|
49
49
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const results = await api.db.execSql({ appId, sql, dbBranch: opts.env });
|
|
54
|
-
const rows = collectSelectRows(results);
|
|
55
|
-
// 单表查询且 _dl_audit_config 没记录 → enabled=false 占位
|
|
50
|
+
const items = await api.db.getAuditStatus({ appId, table, dbBranch: opts.env });
|
|
51
|
+
const rows = items.map(toAuditStatus);
|
|
52
|
+
// 单表查询但后端没记录 → 占位 enabled=false
|
|
56
53
|
if (table !== undefined && table !== "" && rows.length === 0) {
|
|
57
|
-
rows.push({ table, enabled: false });
|
|
54
|
+
rows.push({ table, enabled: false, enabled_at: null, retention: null });
|
|
58
55
|
}
|
|
59
|
-
const items = rows.map(toAuditStatus);
|
|
60
56
|
// PRD JSON:单表返 object,多表返 array
|
|
61
57
|
if ((0, output_1.isJsonMode)()) {
|
|
62
|
-
if (table !== undefined &&
|
|
63
|
-
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(
|
|
58
|
+
if (table !== undefined && rows.length === 1) {
|
|
59
|
+
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(rows[0]));
|
|
64
60
|
}
|
|
65
61
|
else {
|
|
66
|
-
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(
|
|
62
|
+
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(rows));
|
|
67
63
|
}
|
|
68
64
|
return;
|
|
69
65
|
}
|
|
70
|
-
if (
|
|
66
|
+
if (rows.length === 0) {
|
|
71
67
|
(0, output_1.emit)("No audit configuration found.");
|
|
72
68
|
return;
|
|
73
69
|
}
|
|
74
70
|
const tty = (0, render_1.isStdoutTty)();
|
|
75
|
-
// 单表 → key:value
|
|
76
|
-
if (table !== undefined &&
|
|
77
|
-
const it =
|
|
71
|
+
// 单表 → key:value 形态
|
|
72
|
+
if (table !== undefined && rows.length === 1) {
|
|
73
|
+
const it = rows[0];
|
|
78
74
|
(0, output_1.emit)((0, render_1.renderKeyValue)([
|
|
79
75
|
["Table", it.table],
|
|
80
76
|
["Enabled", boolToYesNo(it.enabled)],
|
|
@@ -85,7 +81,7 @@ async function handleDbAuditStatus(table, opts) {
|
|
|
85
81
|
}
|
|
86
82
|
// 列表 → table 形态
|
|
87
83
|
const headers = ["table", "enabled", "enabled_at", "retention"];
|
|
88
|
-
const out =
|
|
84
|
+
const out = rows.map((it) => [
|
|
89
85
|
it.table,
|
|
90
86
|
boolToYesNo(it.enabled),
|
|
91
87
|
it.enabled_at ? (0, render_1.formatTime)(it.enabled_at, tty) : "—",
|
|
@@ -93,24 +89,12 @@ async function handleDbAuditStatus(table, opts) {
|
|
|
93
89
|
]);
|
|
94
90
|
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(headers, out) : (0, render_1.renderTsv)(headers, out));
|
|
95
91
|
}
|
|
96
|
-
function toAuditStatus(
|
|
97
|
-
// retention 列存的是后端 ValidDay (int64 天数),按 sentinel 反向映射成 PRD 字符串
|
|
98
|
-
const rawRetention = row.retention;
|
|
99
|
-
let retention = null;
|
|
100
|
-
if (rawRetention != null) {
|
|
101
|
-
if (typeof rawRetention === "number") {
|
|
102
|
-
retention = retentionDaysToString(rawRetention);
|
|
103
|
-
}
|
|
104
|
-
else if (typeof rawRetention === "string") {
|
|
105
|
-
// 兼容存的就是字符串形态("7d" / "forever")
|
|
106
|
-
retention = rawRetention;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
92
|
+
function toAuditStatus(s) {
|
|
109
93
|
return {
|
|
110
|
-
table:
|
|
111
|
-
enabled:
|
|
112
|
-
enabled_at:
|
|
113
|
-
retention,
|
|
94
|
+
table: s.table,
|
|
95
|
+
enabled: s.enabled,
|
|
96
|
+
enabled_at: s.enabledAt ?? null,
|
|
97
|
+
retention: s.retention ?? null,
|
|
114
98
|
};
|
|
115
99
|
}
|
|
116
100
|
async function handleDbAuditEnable(table, opts) {
|
|
@@ -146,7 +130,9 @@ async function handleDbAuditEnable(table, opts) {
|
|
|
146
130
|
}
|
|
147
131
|
const tty = (0, render_1.isStdoutTty)();
|
|
148
132
|
const prefix = tty ? "✓" : "OK";
|
|
149
|
-
|
|
133
|
+
// c.highlight 内部按 TTY/NO_COLOR/FORCE_COLOR 自动决定是否染色,无需再判 tty
|
|
134
|
+
const tableLabel = colors_1.c.highlight(`'${status.table}'`);
|
|
135
|
+
(0, output_1.emit)(`${prefix} Audit enabled for table ${tableLabel} (retention: ${status.retention ?? retention})`);
|
|
150
136
|
}
|
|
151
137
|
async function handleDbAuditDisable(table, opts) {
|
|
152
138
|
if (!table) {
|
|
@@ -170,7 +156,7 @@ async function handleDbAuditDisable(table, opts) {
|
|
|
170
156
|
}
|
|
171
157
|
const tty = (0, render_1.isStdoutTty)();
|
|
172
158
|
const prefix = tty ? "✓" : "OK";
|
|
173
|
-
(0, output_1.emit)(`${prefix} Audit disabled for table '${status.table}'`);
|
|
159
|
+
(0, output_1.emit)(`${prefix} Audit disabled for table ${colors_1.c.highlight(`'${status.table}'`)}`);
|
|
174
160
|
}
|
|
175
161
|
async function handleDbAuditList(tables, opts) {
|
|
176
162
|
if (tables.length === 0) {
|
|
@@ -182,40 +168,35 @@ async function handleDbAuditList(tables, opts) {
|
|
|
182
168
|
}
|
|
183
169
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
184
170
|
const limit = opts.limit ?? 20;
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
const since = normalizeTime(
|
|
171
|
+
// 时间字段归一化为 ISO 8601 UTC(服务端按 event_time 字段比较)。cursor 由
|
|
172
|
+
// 后端管理(本质也是 ISO 时间),CLI 不再混用 cursor/since。
|
|
173
|
+
const since = normalizeTime(opts.since, "--since");
|
|
188
174
|
const until = normalizeTime(opts.until, "--until");
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const visible = hasMore ? allRows.slice(0, limit) : allRows;
|
|
201
|
-
const nextCursor = hasMore && visible.length > 0 ? asString(visible[visible.length - 1].event_time) : null;
|
|
202
|
-
// 多表混合时统计哪些表无记录
|
|
203
|
-
const seen = new Set();
|
|
204
|
-
for (const r of visible)
|
|
205
|
-
seen.add(asString(r.target_table));
|
|
206
|
-
const skipped = tables.filter((t) => !seen.has(t));
|
|
175
|
+
const result = await api.db.listAuditLog({
|
|
176
|
+
appId,
|
|
177
|
+
tables,
|
|
178
|
+
since,
|
|
179
|
+
until,
|
|
180
|
+
limit,
|
|
181
|
+
cursor: opts.cursor,
|
|
182
|
+
dbBranch: opts.env,
|
|
183
|
+
});
|
|
184
|
+
const visible = result.items;
|
|
185
|
+
const skipped = result.skipped;
|
|
207
186
|
// PRD JSON:data 是数组,元素是事件对象(snake_case),分页信封 next_cursor / has_more
|
|
208
187
|
if ((0, output_1.isJsonMode)()) {
|
|
209
|
-
const items = visible.map((
|
|
210
|
-
event_id:
|
|
211
|
-
event_time:
|
|
212
|
-
target_table:
|
|
213
|
-
type:
|
|
214
|
-
operator:
|
|
215
|
-
summary:
|
|
216
|
-
|
|
188
|
+
const items = visible.map((it) => ({
|
|
189
|
+
event_id: it.eventId,
|
|
190
|
+
event_time: it.eventTime,
|
|
191
|
+
target_table: it.targetTable,
|
|
192
|
+
type: it.type,
|
|
193
|
+
operator: it.operator,
|
|
194
|
+
summary: it.summary,
|
|
195
|
+
// before/after 服务端是 JSON 字符串,反序列化回结构化对象供下游消费
|
|
196
|
+
...(it.before !== undefined ? { before: safeParseJson(it.before) } : {}),
|
|
197
|
+
...(it.after !== undefined ? { after: safeParseJson(it.after) } : {}),
|
|
217
198
|
}));
|
|
218
|
-
(0, output_1.emitPaged)(items, nextCursor, hasMore);
|
|
199
|
+
(0, output_1.emitPaged)(items, result.nextCursor, result.hasMore);
|
|
219
200
|
if (skipped.length > 0) {
|
|
220
201
|
process.stderr.write(`(${String(skipped.length)} table(s) skipped: ${skipped.join(", ")})\n`);
|
|
221
202
|
}
|
|
@@ -238,23 +219,17 @@ async function handleDbAuditList(tables, opts) {
|
|
|
238
219
|
? ["target_table", "event_time", "type", "event_id", "operator", "summary"]
|
|
239
220
|
: ["event_time", "type", "event_id", "operator", "summary"];
|
|
240
221
|
const rows = visible.map((it) => {
|
|
241
|
-
const eventTime = (0, render_1.formatTime)(
|
|
242
|
-
const eventId = truncateId(
|
|
243
|
-
const cells = [
|
|
244
|
-
|
|
245
|
-
asString(it.type),
|
|
246
|
-
eventId,
|
|
247
|
-
asString(it.operator, "—"),
|
|
248
|
-
asString(it.summary),
|
|
249
|
-
];
|
|
250
|
-
return isMultiTable ? [asString(it.target_table), ...cells] : cells;
|
|
222
|
+
const eventTime = (0, render_1.formatTime)(it.eventTime, tty);
|
|
223
|
+
const eventId = truncateId(it.eventId);
|
|
224
|
+
const cells = [eventTime, it.type, eventId, it.operator || "—", it.summary];
|
|
225
|
+
return isMultiTable ? [it.targetTable, ...cells] : cells;
|
|
251
226
|
});
|
|
252
227
|
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(headers, rows) : (0, render_1.renderTsv)(headers, rows));
|
|
253
228
|
if (skipped.length > 0) {
|
|
254
229
|
process.stderr.write(`(${String(skipped.length)} table(s) skipped: ${skipped.join(", ")})\n`);
|
|
255
230
|
}
|
|
256
|
-
if (hasMore && nextCursor) {
|
|
257
|
-
process.stderr.write(`(more results; use --cursor '${nextCursor}')\n`);
|
|
231
|
+
if (result.hasMore && result.nextCursor) {
|
|
232
|
+
process.stderr.write(`(more results; use --cursor '${result.nextCursor}')\n`);
|
|
258
233
|
}
|
|
259
234
|
}
|
|
260
235
|
// ── helpers ──
|
|
@@ -268,46 +243,20 @@ function normalizeTime(input, flagName) {
|
|
|
268
243
|
const ms = (0, index_1.parseTimeFilterMs)(input, flagName);
|
|
269
244
|
return new Date(ms).toISOString();
|
|
270
245
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
246
|
+
// 服务端 before/after 是 JSON 字符串透传,CLI JSON 输出再反序列化回对象,
|
|
247
|
+
// 给下游 jq 等工具消费。失败时透传原始字符串避免数据丢失。
|
|
248
|
+
function safeParseJson(s) {
|
|
249
|
+
try {
|
|
250
|
+
return JSON.parse(s);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return s;
|
|
277
254
|
}
|
|
278
|
-
return out;
|
|
279
|
-
}
|
|
280
|
-
// SQL 字面量转义。表名 / 时间戳由调用方控制范围(白名单或严格 ISO 8601 正则)。
|
|
281
|
-
function escapeSqlLiteral(s) {
|
|
282
|
-
return s.replace(/'/g, "''");
|
|
283
255
|
}
|
|
284
256
|
// PRD 输出 enabled 列用 yes/no 而非 true/false
|
|
285
257
|
function boolToYesNo(b) {
|
|
286
258
|
return b ? "yes" : "no";
|
|
287
259
|
}
|
|
288
|
-
// 后端 ValidDay (int64 天数) → PRD retention 字符串。-1 = 永久(前端 / 后端约定)
|
|
289
|
-
function retentionDaysToString(days) {
|
|
290
|
-
if (days === -1)
|
|
291
|
-
return "forever";
|
|
292
|
-
if (days === 7)
|
|
293
|
-
return "7d";
|
|
294
|
-
if (days === 30)
|
|
295
|
-
return "30d";
|
|
296
|
-
if (days === 180)
|
|
297
|
-
return "180d";
|
|
298
|
-
if (days === 360)
|
|
299
|
-
return "360d";
|
|
300
|
-
return `${String(days)}d`;
|
|
301
|
-
}
|
|
302
|
-
function asString(v, fallback = "") {
|
|
303
|
-
if (v == null)
|
|
304
|
-
return fallback;
|
|
305
|
-
if (typeof v === "string")
|
|
306
|
-
return v;
|
|
307
|
-
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint")
|
|
308
|
-
return String(v);
|
|
309
|
-
return JSON.stringify(v);
|
|
310
|
-
}
|
|
311
260
|
// audit list 的 event_id 在 TTY 视图里截断展示(PRD 示例 "01525416B44F...")
|
|
312
261
|
function truncateId(id) {
|
|
313
262
|
if (id.length <= 12)
|
|
@@ -42,7 +42,17 @@ async function handleDbQuota(opts) {
|
|
|
42
42
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
43
43
|
const data = await api.db.getDbQuota({ appId, dbBranch: opts.env });
|
|
44
44
|
if ((0, output_1.isJsonMode)()) {
|
|
45
|
-
|
|
45
|
+
// 配额未对接(storageQuotaBytes=0)时,quota / usage_percent 字段都不输出
|
|
46
|
+
const out = {
|
|
47
|
+
storageUsedBytes: data.storageUsedBytes,
|
|
48
|
+
tables: data.tables,
|
|
49
|
+
views: data.views,
|
|
50
|
+
};
|
|
51
|
+
if (data.storageQuotaBytes > 0) {
|
|
52
|
+
out.storageQuotaBytes = data.storageQuotaBytes;
|
|
53
|
+
out.usagePercent = data.usagePercent;
|
|
54
|
+
}
|
|
55
|
+
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(out));
|
|
46
56
|
return;
|
|
47
57
|
}
|
|
48
58
|
// PRD:单行 "Storage: 14.9 MB / 1 GB (1.5%)";配额未对接时只显示 used
|
|
@@ -42,7 +42,16 @@ async function handleFileQuota(opts) {
|
|
|
42
42
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
43
43
|
const data = await api.file.getStorageQuota({ appId });
|
|
44
44
|
if ((0, output_1.isJsonMode)()) {
|
|
45
|
-
|
|
45
|
+
// 配额未对接(storageQuotaBytes=0)时,quota / usage_percent 字段都不输出
|
|
46
|
+
const out = {
|
|
47
|
+
storageUsedBytes: data.storageUsedBytes,
|
|
48
|
+
files: data.files,
|
|
49
|
+
};
|
|
50
|
+
if (data.storageQuotaBytes > 0) {
|
|
51
|
+
out.storageQuotaBytes = data.storageQuotaBytes;
|
|
52
|
+
out.usagePercent = data.usagePercent;
|
|
53
|
+
}
|
|
54
|
+
(0, output_1.emitOk)((0, output_1.snakeCaseKeys)(out));
|
|
46
55
|
return;
|
|
47
56
|
}
|
|
48
57
|
// PRD:单行 "Storage: 150 MB / 1 GB (15%)";配额未对接时只显示 used
|