@lark-apaas/miaoda-cli 0.1.2-alpha.8bc93eb → 0.1.2-alpha.96b37fa
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 +46 -0
- package/dist/api/db/index.js +2 -1
- package/dist/cli/handlers/db/audit.js +39 -69
- package/package.json +1 -1
package/dist/api/db/api.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.exportData = exportData;
|
|
|
7
7
|
exports.listDDLChangelog = listDDLChangelog;
|
|
8
8
|
exports.getAuditStatus = getAuditStatus;
|
|
9
9
|
exports.setAuditConfig = setAuditConfig;
|
|
10
|
+
exports.listAuditLog = listAuditLog;
|
|
10
11
|
exports.migrationInit = migrationInit;
|
|
11
12
|
exports.migrate = migrate;
|
|
12
13
|
exports.recover = recover;
|
|
@@ -392,6 +393,51 @@ async function setAuditConfig(opts) {
|
|
|
392
393
|
}
|
|
393
394
|
return data.status;
|
|
394
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
|
+
}
|
|
395
441
|
// ── db migration → InnerAdminMigrationInit / InnerAdminMigrate ──
|
|
396
442
|
/**
|
|
397
443
|
* 后端:POST /v1/dataloom/app/{appId}/db/enableMultiEnv
|
package/dist/api/db/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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.getAuditStatus = 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; } });
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "exportData", { enumerable: true, get: function (
|
|
|
9
9
|
Object.defineProperty(exports, "listDDLChangelog", { enumerable: true, get: function () { return api_1.listDDLChangelog; } });
|
|
10
10
|
Object.defineProperty(exports, "getAuditStatus", { enumerable: true, get: function () { return api_1.getAuditStatus; } });
|
|
11
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; } });
|
|
12
13
|
Object.defineProperty(exports, "migrationInit", { enumerable: true, get: function () { return api_1.migrationInit; } });
|
|
13
14
|
Object.defineProperty(exports, "migrate", { enumerable: true, get: function () { return api_1.migrate; } });
|
|
14
15
|
Object.defineProperty(exports, "recover", { enumerable: true, get: function () { return api_1.recover; } });
|
|
@@ -44,7 +44,6 @@ const colors_1 = require("../../../utils/colors");
|
|
|
44
44
|
const error_1 = require("../../../utils/error");
|
|
45
45
|
const output_1 = require("../../../utils/output");
|
|
46
46
|
const render_1 = require("../../../utils/render");
|
|
47
|
-
const index_2 = require("../../../api/db/index");
|
|
48
47
|
const VALID_RETENTION = new Set(["7d", "30d", "180d", "360d", "forever"]);
|
|
49
48
|
async function handleDbAuditStatus(table, opts) {
|
|
50
49
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
@@ -169,40 +168,35 @@ async function handleDbAuditList(tables, opts) {
|
|
|
169
168
|
}
|
|
170
169
|
const appId = (0, shared_1.resolveAppId)(opts);
|
|
171
170
|
const limit = opts.limit ?? 20;
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
const since = normalizeTime(
|
|
171
|
+
// 时间字段归一化为 ISO 8601 UTC(服务端按 event_time 字段比较)。cursor 由
|
|
172
|
+
// 后端管理(本质也是 ISO 时间),CLI 不再混用 cursor/since。
|
|
173
|
+
const since = normalizeTime(opts.since, "--since");
|
|
175
174
|
const until = normalizeTime(opts.until, "--until");
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
const visible = hasMore ? allRows.slice(0, limit) : allRows;
|
|
188
|
-
const nextCursor = hasMore && visible.length > 0 ? asString(visible[visible.length - 1].event_time) : null;
|
|
189
|
-
// 多表混合时统计哪些表无记录
|
|
190
|
-
const seen = new Set();
|
|
191
|
-
for (const r of visible)
|
|
192
|
-
seen.add(asString(r.target_table));
|
|
193
|
-
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;
|
|
194
186
|
// PRD JSON:data 是数组,元素是事件对象(snake_case),分页信封 next_cursor / has_more
|
|
195
187
|
if ((0, output_1.isJsonMode)()) {
|
|
196
|
-
const items = visible.map((
|
|
197
|
-
event_id:
|
|
198
|
-
event_time:
|
|
199
|
-
target_table:
|
|
200
|
-
type:
|
|
201
|
-
operator:
|
|
202
|
-
summary:
|
|
203
|
-
|
|
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) } : {}),
|
|
204
198
|
}));
|
|
205
|
-
(0, output_1.emitPaged)(items, nextCursor, hasMore);
|
|
199
|
+
(0, output_1.emitPaged)(items, result.nextCursor, result.hasMore);
|
|
206
200
|
if (skipped.length > 0) {
|
|
207
201
|
process.stderr.write(`(${String(skipped.length)} table(s) skipped: ${skipped.join(", ")})\n`);
|
|
208
202
|
}
|
|
@@ -225,23 +219,17 @@ async function handleDbAuditList(tables, opts) {
|
|
|
225
219
|
? ["target_table", "event_time", "type", "event_id", "operator", "summary"]
|
|
226
220
|
: ["event_time", "type", "event_id", "operator", "summary"];
|
|
227
221
|
const rows = visible.map((it) => {
|
|
228
|
-
const eventTime = (0, render_1.formatTime)(
|
|
229
|
-
|
|
230
|
-
const cells = [
|
|
231
|
-
|
|
232
|
-
asString(it.type),
|
|
233
|
-
eventId,
|
|
234
|
-
asString(it.operator, "—"),
|
|
235
|
-
asString(it.summary),
|
|
236
|
-
];
|
|
237
|
-
return isMultiTable ? [asString(it.target_table), ...cells] : cells;
|
|
222
|
+
const eventTime = (0, render_1.formatTime)(it.eventTime, tty);
|
|
223
|
+
// event_id 完整透传——PRD 截图里的 "..." 只是文档省略写法,不是 CLI 行为
|
|
224
|
+
const cells = [eventTime, it.type, it.eventId, it.operator || "—", it.summary];
|
|
225
|
+
return isMultiTable ? [it.targetTable, ...cells] : cells;
|
|
238
226
|
});
|
|
239
227
|
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(headers, rows) : (0, render_1.renderTsv)(headers, rows));
|
|
240
228
|
if (skipped.length > 0) {
|
|
241
229
|
process.stderr.write(`(${String(skipped.length)} table(s) skipped: ${skipped.join(", ")})\n`);
|
|
242
230
|
}
|
|
243
|
-
if (hasMore && nextCursor) {
|
|
244
|
-
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`);
|
|
245
233
|
}
|
|
246
234
|
}
|
|
247
235
|
// ── helpers ──
|
|
@@ -255,35 +243,17 @@ function normalizeTime(input, flagName) {
|
|
|
255
243
|
const ms = (0, index_1.parseTimeFilterMs)(input, flagName);
|
|
256
244
|
return new Date(ms).toISOString();
|
|
257
245
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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;
|
|
264
254
|
}
|
|
265
|
-
return out;
|
|
266
|
-
}
|
|
267
|
-
// SQL 字面量转义。表名 / 时间戳由调用方控制范围(白名单或严格 ISO 8601 正则)。
|
|
268
|
-
function escapeSqlLiteral(s) {
|
|
269
|
-
return s.replace(/'/g, "''");
|
|
270
255
|
}
|
|
271
256
|
// PRD 输出 enabled 列用 yes/no 而非 true/false
|
|
272
257
|
function boolToYesNo(b) {
|
|
273
258
|
return b ? "yes" : "no";
|
|
274
259
|
}
|
|
275
|
-
function asString(v, fallback = "") {
|
|
276
|
-
if (v == null)
|
|
277
|
-
return fallback;
|
|
278
|
-
if (typeof v === "string")
|
|
279
|
-
return v;
|
|
280
|
-
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint")
|
|
281
|
-
return String(v);
|
|
282
|
-
return JSON.stringify(v);
|
|
283
|
-
}
|
|
284
|
-
// audit list 的 event_id 在 TTY 视图里截断展示(PRD 示例 "01525416B44F...")
|
|
285
|
-
function truncateId(id) {
|
|
286
|
-
if (id.length <= 12)
|
|
287
|
-
return id;
|
|
288
|
-
return id.slice(0, 12) + "...";
|
|
289
|
-
}
|