@lark-apaas/miaoda-cli 0.1.3 → 0.1.4
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/app/api.js +3 -3
- package/dist/api/app/schemas.js +43 -43
- package/dist/api/db/api.js +398 -55
- package/dist/api/db/client.js +155 -28
- package/dist/api/db/index.js +12 -1
- package/dist/api/db/parsers.js +20 -20
- package/dist/api/db/sql-keywords.js +87 -87
- package/dist/api/deploy/api.js +5 -5
- package/dist/api/deploy/schemas.js +32 -32
- package/dist/api/file/api.js +89 -87
- package/dist/api/file/client.js +62 -22
- package/dist/api/file/detect.js +3 -3
- package/dist/api/file/index.js +2 -1
- package/dist/api/file/parsers.js +18 -7
- package/dist/api/observability/api.js +6 -6
- package/dist/api/observability/schemas.js +14 -14
- package/dist/api/plugin/api.js +31 -31
- package/dist/cli/commands/app/index.js +12 -12
- package/dist/cli/commands/db/index.js +602 -54
- package/dist/cli/commands/deploy/index.js +28 -28
- package/dist/cli/commands/file/index.js +85 -58
- package/dist/cli/commands/observability/index.js +69 -69
- package/dist/cli/commands/plugin/index.js +27 -27
- package/dist/cli/commands/shared.js +10 -10
- package/dist/cli/handlers/app/update.js +2 -2
- package/dist/cli/handlers/db/_destructive.js +67 -0
- package/dist/cli/handlers/db/_env.js +26 -0
- package/dist/cli/handlers/db/_operator.js +35 -0
- package/dist/cli/handlers/db/audit.js +383 -0
- package/dist/cli/handlers/db/changelog.js +160 -0
- package/dist/cli/handlers/db/data.js +32 -31
- package/dist/cli/handlers/db/index.js +17 -1
- package/dist/cli/handlers/db/migration.js +234 -0
- package/dist/cli/handlers/db/quota.js +68 -0
- package/dist/cli/handlers/db/recovery.js +413 -0
- package/dist/cli/handlers/db/schema.js +33 -33
- package/dist/cli/handlers/db/sql.js +69 -69
- package/dist/cli/handlers/deploy/deploy.js +4 -4
- package/dist/cli/handlers/deploy/error-log.js +1 -1
- package/dist/cli/handlers/deploy/get.js +3 -3
- package/dist/cli/handlers/deploy/polling.js +11 -11
- package/dist/cli/handlers/file/cp.js +30 -30
- package/dist/cli/handlers/file/index.js +3 -1
- package/dist/cli/handlers/file/ls.js +5 -5
- package/dist/cli/handlers/file/quota.js +66 -0
- package/dist/cli/handlers/file/rm.js +32 -30
- package/dist/cli/handlers/file/sign.js +3 -3
- package/dist/cli/handlers/file/stat.js +10 -9
- package/dist/cli/handlers/observability/analytics.js +47 -47
- package/dist/cli/handlers/observability/helpers.js +2 -2
- package/dist/cli/handlers/observability/log.js +9 -9
- package/dist/cli/handlers/observability/metric.js +26 -26
- package/dist/cli/handlers/observability/trace.js +5 -5
- package/dist/cli/handlers/plugin/plugin-local.js +53 -53
- package/dist/cli/handlers/plugin/plugin.js +15 -15
- package/dist/cli/help.js +16 -16
- package/dist/main.js +12 -12
- package/dist/utils/args.js +1 -1
- package/dist/utils/colors.js +2 -2
- package/dist/utils/config.js +2 -2
- package/dist/utils/devops-error.js +9 -9
- package/dist/utils/error.js +2 -2
- package/dist/utils/git.js +4 -4
- package/dist/utils/http.js +19 -19
- package/dist/utils/index.js +3 -1
- package/dist/utils/output.js +67 -45
- package/dist/utils/poll.js +35 -0
- package/dist/utils/render.js +27 -27
- package/dist/utils/spinner.js +46 -0
- package/dist/utils/time.js +47 -42
- package/package.json +1 -1
package/dist/api/db/client.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
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;
|
|
7
|
+
exports.buildUnknownEnvError = buildUnknownEnvError;
|
|
8
|
+
exports.decorateEnvError = decorateEnvError;
|
|
7
9
|
exports.extractData = extractData;
|
|
8
10
|
exports.buildInnerUrl = buildInnerUrl;
|
|
9
11
|
const error_1 = require("../../utils/error");
|
|
@@ -25,9 +27,9 @@ function traceHttp(method, url, start, response, err) {
|
|
|
25
27
|
try {
|
|
26
28
|
const cost = Date.now() - start;
|
|
27
29
|
const status = response?.status ?? 0;
|
|
28
|
-
const logid = response?.headers?.get?.(
|
|
30
|
+
const logid = response?.headers?.get?.('x-tt-logid') ?? '-';
|
|
29
31
|
if (err !== undefined) {
|
|
30
|
-
const errMsg = err instanceof Error ? err.message : typeof err ===
|
|
32
|
+
const errMsg = err instanceof Error ? err.message : typeof err === 'string' ? err : JSON.stringify(err);
|
|
31
33
|
(0, logger_1.debug)(`http ${method} ${url} ${String(status)} cost=${String(cost)}ms x-tt-logid=${logid} err=${errMsg}`);
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
@@ -46,16 +48,16 @@ function traceHttp(method, url, start, response, err) {
|
|
|
46
48
|
*/
|
|
47
49
|
function ensureInnerSuccess(body) {
|
|
48
50
|
if (!body) {
|
|
49
|
-
throw new error_1.AppError(
|
|
51
|
+
throw new error_1.AppError('INTERNAL_DB_ERROR', 'empty response body');
|
|
50
52
|
}
|
|
51
|
-
const code = body.status_code ?? body.ErrorCode ??
|
|
52
|
-
if (code ===
|
|
53
|
+
const code = body.status_code ?? body.ErrorCode ?? '0';
|
|
54
|
+
if (code === '0' || code === '')
|
|
53
55
|
return;
|
|
54
56
|
const appErr = mapDataloomBizError(code, body.error_msg ?? body.ErrorMessage ?? `dataloom API error [${code}]`);
|
|
55
57
|
// PRD 多语句失败:后端在 envelope 顶层透出 errorStatementIndex(从 0 起计),
|
|
56
58
|
// 单语句 / 单元执行不会带这个字段。挂到 AppError 上让 emitError 落到错误信封
|
|
57
59
|
// statement_index 字段。
|
|
58
|
-
if (typeof body.errorStatementIndex ===
|
|
60
|
+
if (typeof body.errorStatementIndex === 'number') {
|
|
59
61
|
appErr.statement_index = body.errorStatementIndex;
|
|
60
62
|
}
|
|
61
63
|
throw appErr;
|
|
@@ -69,7 +71,8 @@ function ensureInnerSuccess(body) {
|
|
|
69
71
|
*
|
|
70
72
|
* 优先级:
|
|
71
73
|
* 1. k_dl_1300002 + SQLSTATE 透传 → SQLSTATE_MAP
|
|
72
|
-
* 2. k_dl_1600000 + "Invalid DB Branch" →
|
|
74
|
+
* 2. k_dl_1600000 + "Invalid DB Branch" → UNKNOWN_ENV_VALUE 占位(mapDbHttpError
|
|
75
|
+
* 会用用户实际 --env 值通过 decorateEnvError 重写)
|
|
73
76
|
* 3. BIZ_ERR_MAP 命中 → 语义 code(可能带 hint)
|
|
74
77
|
* 4. 兜底 DB_API_<code>
|
|
75
78
|
*
|
|
@@ -77,14 +80,20 @@ function ensureInnerSuccess(body) {
|
|
|
77
80
|
*/
|
|
78
81
|
function mapDataloomBizError(code, rawMessage) {
|
|
79
82
|
const message = stripPgPrefix(rawMessage);
|
|
80
|
-
if (code ===
|
|
83
|
+
if (code === 'k_dl_1300002') {
|
|
81
84
|
const sqlstate = extractSqlstate(message);
|
|
82
85
|
if (sqlstate && exports.SQLSTATE_MAP[sqlstate]) {
|
|
83
86
|
return new error_1.AppError(exports.SQLSTATE_MAP[sqlstate], message);
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
|
-
if (code ===
|
|
87
|
-
|
|
89
|
+
if (code === 'k_dl_1600000' && message.startsWith('Invalid DB Branch')) {
|
|
90
|
+
// 这里没有 user-facing --env 值(client 层不知道 CLI 入参),先用 server 侧返回的
|
|
91
|
+
// 分支名占位;mapDbHttpError 在拿到 env 上下文后会通过 decorateEnvError 重写成
|
|
92
|
+
// 用户传入的实际值。dataloom 错误文案历史上同时存在英文冒号和中文冒号「:」格式,
|
|
93
|
+
// 这里两种都接。
|
|
94
|
+
const m = /Invalid DB Branch[::\s]+([^\s]+)/i.exec(message);
|
|
95
|
+
const branch = m?.[1] ?? '<branch>';
|
|
96
|
+
return buildUnknownEnvError(branch);
|
|
88
97
|
}
|
|
89
98
|
const mapped = BIZ_ERR_MAP.get(code);
|
|
90
99
|
if (mapped) {
|
|
@@ -94,13 +103,78 @@ function mapDataloomBizError(code, rawMessage) {
|
|
|
94
103
|
}
|
|
95
104
|
return new error_1.AppError(`DB_API_${code}`, message);
|
|
96
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
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* 统一构造"--env 值不可用"错误。两类入口共用:
|
|
116
|
+
* - CLI vocab 校验(_env.ts validateEnv):用户传 dev/main/online 以外值
|
|
117
|
+
* - backend state 错(decorateEnvError):vocab 合法但 db 实际状态对不上
|
|
118
|
+
*
|
|
119
|
+
* `available` 跟 `suggestInit` 由调用方按错误码归类传入;user-facing 不展示 `main`
|
|
120
|
+
* 内部别名,统一用 `online`。
|
|
121
|
+
*/
|
|
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
|
+
}
|
|
129
|
+
return new error_1.AppError('UNKNOWN_ENV_VALUE', `Unknown --env value '${env}'`, {
|
|
130
|
+
next_actions: hints,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 把 backend env 相关错误统一重写成 UNKNOWN_ENV_VALUE 文案,按错误码做 state 推断:
|
|
135
|
+
*
|
|
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
|
|
140
|
+
*
|
|
141
|
+
* env 未传时不改写:避免把 backend 兜底文案(如 "Multi-env is not initialized")
|
|
142
|
+
* 改成 `Unknown --env value ''` 这种空字符串错误。
|
|
143
|
+
*/
|
|
144
|
+
function decorateEnvError(err, env) {
|
|
145
|
+
if (env === undefined || env === '')
|
|
146
|
+
return err;
|
|
147
|
+
const ctx = classifyEnvError(err);
|
|
148
|
+
if (!ctx)
|
|
149
|
+
return err;
|
|
150
|
+
return buildUnknownEnvError(env, ctx);
|
|
151
|
+
}
|
|
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;
|
|
170
|
+
}
|
|
97
171
|
/**
|
|
98
172
|
* 剥掉 PG 透传错误的 "ERROR:" 前缀:CLI 输出本身就会前缀 "Error:",
|
|
99
173
|
* 不去掉就会变成 `Error: ERROR: relation ...` 双重前缀,PRD 不要这种冗余。
|
|
100
174
|
* 大小写敏感,只匹配 PG 标准格式。
|
|
101
175
|
*/
|
|
102
176
|
function stripPgPrefix(msg) {
|
|
103
|
-
return msg.replace(/^ERROR:\s*/,
|
|
177
|
+
return msg.replace(/^ERROR:\s*/, '');
|
|
104
178
|
}
|
|
105
179
|
/** 从 PG 执行错误消息里提取 "(SQLSTATE XXXXX)"。 */
|
|
106
180
|
function extractSqlstate(msg) {
|
|
@@ -117,27 +191,63 @@ const BIZ_ERR_MAP = new Map(Object.entries({
|
|
|
117
191
|
// 来源:真实环境探测(dataloom InnerExecuteSQL / InnerGetSchema)
|
|
118
192
|
// k_dl_000001:DB 连接 / QPS 限流 等基础设施层错误
|
|
119
193
|
k_dl_000001: {
|
|
120
|
-
code:
|
|
121
|
-
hint:
|
|
194
|
+
code: 'INTERNAL_DB_ERROR',
|
|
195
|
+
hint: '检查 dbBranch 与应用 PG 实例状态,或稍后重试',
|
|
122
196
|
},
|
|
123
197
|
// k_dl_000002:SQL 解析 / 不支持的 SQL 类型(VACUUM、COPY、SET、SHOW 等)
|
|
124
|
-
k_dl_000002: { code:
|
|
198
|
+
k_dl_000002: { code: 'SQL_SYNTAX_ERROR' },
|
|
125
199
|
// k_dl_000003:查询命中系统表(pg_tables / pg_user 等)被拒
|
|
126
|
-
k_dl_000003: { code:
|
|
200
|
+
k_dl_000003: { code: 'SQL_OPERATION_FORBIDDEN' },
|
|
127
201
|
// k_dl_1300002:PG 执行错误;实际 SQLSTATE 由 extractSqlstate 单独映射
|
|
128
202
|
// 未匹配到 SQLSTATE 时走下面兜底 DB_API_<code>
|
|
129
203
|
// k_dl_1300015:SELECT 结果超过 1000 行硬拦;多行 hint 由 output.ts 的
|
|
130
204
|
// SERVER_ERROR_HINTS 按语义 code 兜底,这里只做 code 改名
|
|
131
|
-
k_dl_1300015: { code:
|
|
205
|
+
k_dl_1300015: { code: 'RESULT_SET_TOO_LARGE' },
|
|
206
|
+
// audit
|
|
207
|
+
k_dl_1310001: {
|
|
208
|
+
code: 'AUDIT_ALREADY_ENABLED',
|
|
209
|
+
hint: 'Use `miaoda db audit status <table>` to confirm current state.',
|
|
210
|
+
},
|
|
211
|
+
k_dl_1310002: {
|
|
212
|
+
code: 'AUDIT_NOT_ENABLED',
|
|
213
|
+
hint: 'Run `miaoda db audit enable <table>` first.',
|
|
214
|
+
},
|
|
215
|
+
k_dl_1310003: {
|
|
216
|
+
code: 'INVALID_RETENTION',
|
|
217
|
+
hint: 'Allowed values: 7d, 30d, 180d, 360d, forever.',
|
|
218
|
+
},
|
|
219
|
+
// migration
|
|
220
|
+
k_dl_1320001: {
|
|
221
|
+
code: 'MIGRATION_NOT_AVAILABLE',
|
|
222
|
+
hint: 'Migration commands require an expert-mode application.',
|
|
223
|
+
},
|
|
224
|
+
k_dl_1320002: { code: 'MULTI_ENV_ALREADY_INITIALIZED' },
|
|
225
|
+
k_dl_1320003: {
|
|
226
|
+
code: 'NO_PENDING_CHANGES',
|
|
227
|
+
hint: 'dev and online schemas are already in sync.',
|
|
228
|
+
},
|
|
229
|
+
// recovery
|
|
230
|
+
k_dl_1330001: {
|
|
231
|
+
code: 'RECOVERY_WINDOW_EXCEEDED',
|
|
232
|
+
hint: 'Pick a timestamp inside the supported recovery window.',
|
|
233
|
+
},
|
|
234
|
+
k_dl_1330002: {
|
|
235
|
+
code: 'RECOVERY_IN_PROGRESS',
|
|
236
|
+
hint: 'Wait for the running recovery to finish, or check its status.',
|
|
237
|
+
},
|
|
238
|
+
k_dl_1330003: {
|
|
239
|
+
code: 'INVALID_TIMESTAMP',
|
|
240
|
+
hint: 'Use ISO 8601 / yyyy-mm-dd / yyyy-mm-dd HH:MM:SS.',
|
|
241
|
+
},
|
|
132
242
|
}));
|
|
133
243
|
/** PG SQLSTATE → CLI code(当前 dataloom 不一定透传,预留未来使用) */
|
|
134
244
|
exports.SQLSTATE_MAP = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
245
|
+
'42601': 'SQL_SYNTAX_ERROR',
|
|
246
|
+
'42P01': 'TABLE_NOT_FOUND',
|
|
247
|
+
'42703': 'COLUMN_NOT_FOUND',
|
|
248
|
+
'57014': 'STATEMENT_TIMEOUT',
|
|
249
|
+
'23505': 'UNIQUE_VIOLATION',
|
|
250
|
+
'22P02': 'TYPE_MISMATCH',
|
|
141
251
|
};
|
|
142
252
|
/**
|
|
143
253
|
* 从 envelope 里取 data 业务字段,校验成功后返回;失败则 throw。
|
|
@@ -145,7 +255,7 @@ exports.SQLSTATE_MAP = {
|
|
|
145
255
|
function extractData(body) {
|
|
146
256
|
ensureInnerSuccess(body);
|
|
147
257
|
if (!body?.data) {
|
|
148
|
-
throw new error_1.AppError(
|
|
258
|
+
throw new error_1.AppError('INTERNAL_DB_ERROR', 'response missing data field');
|
|
149
259
|
}
|
|
150
260
|
return body.data;
|
|
151
261
|
}
|
|
@@ -161,13 +271,30 @@ function extractData(body) {
|
|
|
161
271
|
* 调用方传入的 path 已包含 `/db` / `/data` 中段,这里只负责拼前缀和 query。
|
|
162
272
|
*/
|
|
163
273
|
function buildInnerUrl(appId, path, query) {
|
|
164
|
-
const normalized = path.startsWith(
|
|
274
|
+
const normalized = path.startsWith('/') ? path : `/${path}`;
|
|
165
275
|
let url = `/v1/dataloom/app/${encodeURIComponent(appId)}${normalized}`;
|
|
166
276
|
if (query) {
|
|
167
277
|
const usp = new URLSearchParams();
|
|
168
|
-
for (const [k,
|
|
169
|
-
if (
|
|
170
|
-
|
|
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 === '')
|
|
293
|
+
continue;
|
|
294
|
+
// dbBranch 兼容用户视角的 `online` 别名 → 后端实际 dbBranch 名为 `main`,
|
|
295
|
+
// 这里集中归一,避免每个 API 函数各自处理。其他值(dev / 自定义分支)原样透传。
|
|
296
|
+
const norm = k === 'dbBranch' && rawV === 'online' ? 'main' : rawV;
|
|
297
|
+
usp.append(k, norm);
|
|
171
298
|
}
|
|
172
299
|
const qs = usp.toString();
|
|
173
300
|
if (qs)
|
package/dist/api/db/index.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
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.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.getRecoveryStatus = exports.getRecoveryPreview = exports.recover = exports.getMigrationStatus = 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
|
+
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; } });
|
|
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; } });
|
|
13
|
+
Object.defineProperty(exports, "migrationInit", { enumerable: true, get: function () { return api_1.migrationInit; } });
|
|
14
|
+
Object.defineProperty(exports, "migrate", { enumerable: true, get: function () { return api_1.migrate; } });
|
|
15
|
+
Object.defineProperty(exports, "getMigrationStatus", { enumerable: true, get: function () { return api_1.getMigrationStatus; } });
|
|
16
|
+
Object.defineProperty(exports, "recover", { enumerable: true, get: function () { return api_1.recover; } });
|
|
17
|
+
Object.defineProperty(exports, "getRecoveryPreview", { enumerable: true, get: function () { return api_1.getRecoveryPreview; } });
|
|
18
|
+
Object.defineProperty(exports, "getRecoveryStatus", { enumerable: true, get: function () { return api_1.getRecoveryStatus; } });
|
|
19
|
+
Object.defineProperty(exports, "getDbQuota", { enumerable: true, get: function () { return api_1.getDbQuota; } });
|
|
9
20
|
var client_1 = require("./client");
|
|
10
21
|
Object.defineProperty(exports, "buildInnerUrl", { enumerable: true, get: function () { return client_1.buildInnerUrl; } });
|
|
11
22
|
Object.defineProperty(exports, "ensureInnerSuccess", { enumerable: true, get: function () { return client_1.ensureInnerSuccess; } });
|
package/dist/api/db/parsers.js
CHANGED
|
@@ -9,7 +9,7 @@ const error_1 = require("../../utils/error");
|
|
|
9
9
|
* 解析 InnerExecuteSQL 的单条 results[](PRD 要求多条语句只取最后一条)。
|
|
10
10
|
*/
|
|
11
11
|
function parseSqlResult(r) {
|
|
12
|
-
if (r.sqlType ===
|
|
12
|
+
if (r.sqlType === 'SELECT') {
|
|
13
13
|
let rows = [];
|
|
14
14
|
if (r.data) {
|
|
15
15
|
try {
|
|
@@ -18,23 +18,23 @@ function parseSqlResult(r) {
|
|
|
18
18
|
rows = parsed;
|
|
19
19
|
}
|
|
20
20
|
catch {
|
|
21
|
-
throw new error_1.AppError(
|
|
21
|
+
throw new error_1.AppError('INTERNAL_DB_ERROR', 'Failed to parse SELECT result JSON');
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
return {
|
|
25
|
-
kind:
|
|
25
|
+
kind: 'select',
|
|
26
26
|
rows,
|
|
27
27
|
recordCount: r.recordCount ?? rows.length,
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
if (r.sqlType ===
|
|
31
|
-
r.sqlType ===
|
|
32
|
-
r.sqlType ===
|
|
33
|
-
r.sqlType ===
|
|
34
|
-
r.sqlType ===
|
|
30
|
+
if (r.sqlType === 'INSERT' ||
|
|
31
|
+
r.sqlType === 'UPDATE' ||
|
|
32
|
+
r.sqlType === 'DELETE' ||
|
|
33
|
+
r.sqlType === 'MERGE' ||
|
|
34
|
+
r.sqlType === 'DML') {
|
|
35
35
|
const affected = r.affectedRows ?? extractRowCount(r.data);
|
|
36
36
|
return {
|
|
37
|
-
kind:
|
|
37
|
+
kind: 'dml',
|
|
38
38
|
// 上面已 narrow,这里 cast 是为了 SqlType 联合里的 (string & {}) 让 TS 无法
|
|
39
39
|
// 自动收窄到字面量集合,不影响运行时安全
|
|
40
40
|
sqlType: r.sqlType,
|
|
@@ -43,7 +43,7 @@ function parseSqlResult(r) {
|
|
|
43
43
|
}
|
|
44
44
|
// DDL or unknown — sqlType 透传后端给的细粒度(CREATE_TABLE / DROP_TABLE / ...
|
|
45
45
|
// / 笼统 "DDL"),CLI JSON 输出直接当 command 用
|
|
46
|
-
return { kind:
|
|
46
|
+
return { kind: 'ddl', sqlType: r.sqlType };
|
|
47
47
|
}
|
|
48
48
|
/** DML 的 data 通常是 `[{"rowCount": N}]`;兜底从这里读影响行数。 */
|
|
49
49
|
function extractRowCount(data) {
|
|
@@ -54,7 +54,7 @@ function extractRowCount(data) {
|
|
|
54
54
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
55
55
|
const first = parsed[0];
|
|
56
56
|
const v = first.rowCount ?? first.affected_rows;
|
|
57
|
-
if (typeof v ===
|
|
57
|
+
if (typeof v === 'number')
|
|
58
58
|
return v;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -87,8 +87,8 @@ function toSummary(t, stats) {
|
|
|
87
87
|
name: t.tableName,
|
|
88
88
|
description: t.comment && t.comment.length > 0 ? t.comment : null,
|
|
89
89
|
columns: (t.fields ?? []).length,
|
|
90
|
-
estimated_row_count: typeof stats?.estimatedRowCount ===
|
|
91
|
-
size_bytes: typeof stats?.sizeBytes ===
|
|
90
|
+
estimated_row_count: typeof stats?.estimatedRowCount === 'number' ? stats.estimatedRowCount : null,
|
|
91
|
+
size_bytes: typeof stats?.sizeBytes === 'number' ? stats.sizeBytes : null,
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
@@ -125,18 +125,18 @@ function toDetail(t, stats) {
|
|
|
125
125
|
if (cols.length === 0)
|
|
126
126
|
continue;
|
|
127
127
|
const kind = i.indexType.toLowerCase();
|
|
128
|
-
if (kind ===
|
|
129
|
-
constraints.push({ type:
|
|
128
|
+
if (kind === 'primary') {
|
|
129
|
+
constraints.push({ type: 'PRIMARY KEY', columns: cols });
|
|
130
130
|
}
|
|
131
|
-
else if (kind ===
|
|
132
|
-
constraints.push({ type:
|
|
131
|
+
else if (kind === 'unique') {
|
|
132
|
+
constraints.push({ type: 'UNIQUE', columns: cols });
|
|
133
133
|
}
|
|
134
134
|
else {
|
|
135
135
|
// foreign / normal / 其它统一进 indexes 段;method 从 indexDef 的 USING 段解析
|
|
136
136
|
indexes.push({
|
|
137
137
|
name: i.indexName,
|
|
138
138
|
columns: cols,
|
|
139
|
-
method: parseIndexMethod(i.indexDef) ??
|
|
139
|
+
method: parseIndexMethod(i.indexDef) ?? 'btree',
|
|
140
140
|
});
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -146,8 +146,8 @@ function toDetail(t, stats) {
|
|
|
146
146
|
columns: (t.fields ?? []).map(toColumn),
|
|
147
147
|
constraints,
|
|
148
148
|
indexes,
|
|
149
|
-
estimated_row_count: typeof stats?.estimatedRowCount ===
|
|
150
|
-
size_bytes: typeof stats?.sizeBytes ===
|
|
149
|
+
estimated_row_count: typeof stats?.estimatedRowCount === 'number' ? stats.estimatedRowCount : null,
|
|
150
|
+
size_bytes: typeof stats?.sizeBytes === 'number' ? stats.sizeBytes : null,
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
function toColumn(f) {
|
|
@@ -21,103 +21,103 @@ exports.SQL_KEYWORDS = void 0;
|
|
|
21
21
|
*/
|
|
22
22
|
exports.SQL_KEYWORDS = [
|
|
23
23
|
// DML 动词
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
'SELECT',
|
|
25
|
+
'INSERT',
|
|
26
|
+
'UPDATE',
|
|
27
|
+
'DELETE',
|
|
28
|
+
'MERGE',
|
|
29
29
|
// FROM / JOIN 系列
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
'FROM',
|
|
31
|
+
'WHERE',
|
|
32
|
+
'JOIN',
|
|
33
|
+
'LEFT',
|
|
34
|
+
'RIGHT',
|
|
35
|
+
'INNER',
|
|
36
|
+
'OUTER',
|
|
37
|
+
'FULL',
|
|
38
|
+
'CROSS',
|
|
39
|
+
'USING',
|
|
40
40
|
// 聚合 / 排序 / 分页
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
'GROUP',
|
|
42
|
+
'ORDER',
|
|
43
|
+
'BY',
|
|
44
|
+
'HAVING',
|
|
45
|
+
'LIMIT',
|
|
46
|
+
'OFFSET',
|
|
47
|
+
'FETCH',
|
|
48
48
|
// 集合操作
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
'UNION',
|
|
50
|
+
'INTERSECT',
|
|
51
|
+
'EXCEPT',
|
|
52
|
+
'DISTINCT',
|
|
53
|
+
'ALL',
|
|
54
54
|
// 别名 / 关联
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
'AS',
|
|
56
|
+
'ON',
|
|
57
57
|
// 操作符词
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
'AND',
|
|
59
|
+
'OR',
|
|
60
|
+
'NOT',
|
|
61
|
+
'IN',
|
|
62
|
+
'IS',
|
|
63
|
+
'NULL',
|
|
64
|
+
'TRUE',
|
|
65
|
+
'FALSE',
|
|
66
|
+
'BETWEEN',
|
|
67
|
+
'LIKE',
|
|
68
|
+
'ILIKE',
|
|
69
|
+
'SIMILAR',
|
|
70
70
|
// DDL
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
'CREATE',
|
|
72
|
+
'ALTER',
|
|
73
|
+
'DROP',
|
|
74
|
+
'TRUNCATE',
|
|
75
|
+
'RENAME',
|
|
76
|
+
'TABLE',
|
|
77
|
+
'INDEX',
|
|
78
|
+
'VIEW',
|
|
79
|
+
'DATABASE',
|
|
80
|
+
'SCHEMA',
|
|
81
|
+
'COLUMN',
|
|
82
|
+
'CONSTRAINT',
|
|
83
|
+
'SEQUENCE',
|
|
84
84
|
// 约束
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
'PRIMARY',
|
|
86
|
+
'KEY',
|
|
87
|
+
'FOREIGN',
|
|
88
|
+
'REFERENCES',
|
|
89
|
+
'UNIQUE',
|
|
90
|
+
'CHECK',
|
|
91
|
+
'DEFAULT',
|
|
92
92
|
// 写入
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
'VALUES',
|
|
94
|
+
'SET',
|
|
95
|
+
'RETURNING',
|
|
96
|
+
'INTO',
|
|
97
97
|
// 事务
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
'BEGIN',
|
|
99
|
+
'COMMIT',
|
|
100
|
+
'ROLLBACK',
|
|
101
|
+
'SAVEPOINT',
|
|
102
|
+
'TRANSACTION',
|
|
103
103
|
// 控制流 / CTE
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
'IF',
|
|
105
|
+
'EXISTS',
|
|
106
|
+
'REPLACE',
|
|
107
|
+
'WITH',
|
|
108
|
+
'RECURSIVE',
|
|
109
|
+
'CASE',
|
|
110
|
+
'WHEN',
|
|
111
|
+
'THEN',
|
|
112
|
+
'ELSE',
|
|
113
|
+
'END',
|
|
114
114
|
// 类型转换 / 时间提取
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
'CAST',
|
|
116
|
+
'EXTRACT',
|
|
117
117
|
// 排序方向
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
'ASC',
|
|
119
|
+
'DESC',
|
|
120
|
+
'NULLS',
|
|
121
|
+
'FIRST',
|
|
122
|
+
'LAST',
|
|
123
123
|
];
|
package/dist/api/deploy/api.js
CHANGED
|
@@ -6,7 +6,7 @@ exports.getErrorLog = getErrorLog;
|
|
|
6
6
|
exports.queryPipelineInstance = queryPipelineInstance;
|
|
7
7
|
const http_1 = require("../../utils/http");
|
|
8
8
|
const devops_error_1 = require("../../utils/devops-error");
|
|
9
|
-
const DEFAULT_ERR_CODE =
|
|
9
|
+
const DEFAULT_ERR_CODE = 'INTERNAL_DEVOPS_ERROR';
|
|
10
10
|
function envelopeOpts(errPrefix) {
|
|
11
11
|
return {
|
|
12
12
|
errPrefix,
|
|
@@ -18,7 +18,7 @@ function envelopeOpts(errPrefix) {
|
|
|
18
18
|
/** POST /v1/devops/app/:appID/release — 触发发布 */
|
|
19
19
|
async function createRelease(req) {
|
|
20
20
|
const url = `/v1/devops/app/${encodeURIComponent(req.appID)}/release`;
|
|
21
|
-
return (0, http_1.postInnerApi)(url, req, envelopeOpts(
|
|
21
|
+
return (0, http_1.postInnerApi)(url, req, envelopeOpts('Failed to create release'));
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* POST /v1/pipeline/app/:appID/instance/list — 分页查询发布历史
|
|
@@ -29,7 +29,7 @@ async function createRelease(req) {
|
|
|
29
29
|
async function listPipelineInstances(req) {
|
|
30
30
|
const url = `/v1/pipeline/app/${encodeURIComponent(req.appID)}/instance/list`;
|
|
31
31
|
return (0, http_1.postInnerApi)(url, req, {
|
|
32
|
-
errPrefix:
|
|
32
|
+
errPrefix: 'Failed to list deploys',
|
|
33
33
|
defaultErrCode: DEFAULT_ERR_CODE,
|
|
34
34
|
});
|
|
35
35
|
}
|
|
@@ -42,7 +42,7 @@ async function listPipelineInstances(req) {
|
|
|
42
42
|
async function getErrorLog(req) {
|
|
43
43
|
const url = `/v1/pipeline/app/${encodeURIComponent(req.appID)}/instance/${encodeURIComponent(req.instanceID)}/error_log`;
|
|
44
44
|
return (0, http_1.getInnerApi)(url, {
|
|
45
|
-
errPrefix:
|
|
45
|
+
errPrefix: 'Failed to get error log',
|
|
46
46
|
defaultErrCode: DEFAULT_ERR_CODE,
|
|
47
47
|
});
|
|
48
48
|
}
|
|
@@ -54,7 +54,7 @@ async function getErrorLog(req) {
|
|
|
54
54
|
async function queryPipelineInstance(req) {
|
|
55
55
|
const url = `/v1/pipeline/app/${encodeURIComponent(req.appID)}/instance/${encodeURIComponent(req.instanceID)}/detail`;
|
|
56
56
|
return (0, http_1.getInnerApi)(url, {
|
|
57
|
-
errPrefix:
|
|
57
|
+
errPrefix: 'Failed to query pipeline',
|
|
58
58
|
defaultErrCode: DEFAULT_ERR_CODE,
|
|
59
59
|
});
|
|
60
60
|
}
|