@cloudbase/manager-node 5.8.9-beta.0 → 5.9.0
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/.codebuddy/rules/error-code-contract.mdc +13 -0
- package/CHANGELOG.md +1 -0
- package/lib/agent/index.js +6 -5
- package/lib/aiModel/index.js +21 -9
- package/lib/cloudApp/index.js +4 -4
- package/lib/cloudrun/index.js +16 -15
- package/lib/common/index.js +1 -1
- package/lib/database/index.js +23 -23
- package/lib/deploy/DatabaseDeployer.js +19 -24
- package/lib/deploy/DeployOrchestrator.js +2 -2
- package/lib/deploy/FunctionDeployer.js +19 -1
- package/lib/deploy/GatewayDeployer.js +5 -5
- package/lib/deploy/StaticDeployer.js +2 -2
- package/lib/deploy/framework.js +2 -1
- package/lib/deploy/function/config-guard.js +23 -3
- package/lib/deploy/function/enterprise-tcr.js +18 -2
- package/lib/docs/index.js +4 -3
- package/lib/env/index.js +16 -16
- package/lib/environment.js +4 -4
- package/lib/environmentManager.js +5 -1
- package/lib/error-docs.js +385 -0
- package/lib/error-types.js +21 -0
- package/lib/error.js +239 -2
- package/lib/function/index.js +135 -41
- package/lib/function/packer.js +1 -1
- package/lib/hosting/index.js +1 -1
- package/lib/index.js +2 -1
- package/lib/integration/crypto.js +4 -3
- package/lib/integration/index.js +1 -1
- package/lib/integration/secretKey.js +4 -4
- package/lib/monitor/index.js +1 -1
- package/lib/mysql/index.js +2 -1
- package/lib/permission/index.js +56 -49
- package/lib/platform/index.js +2 -2
- package/lib/sandbox/index.js +68 -61
- package/lib/storage/index.js +164 -162
- package/lib/user/index.js +32 -31
- package/lib/utils/cloud-api-request.js +13 -5
- package/lib/utils/cloudbase-request.js +21 -0
- package/lib/utils/fs.js +3 -3
- package/lib/utils/http-request.js +2 -2
- package/lib/utils/index.js +4 -3
- package/lib/utils/storage-config.js +3 -3
- package/package.json +2 -2
- package/types/error-docs.d.ts +57 -0
- package/types/error-types.d.ts +32 -0
- package/types/error.d.ts +148 -2
- package/types/function/index.d.ts +14 -1
- package/types/utils/cloudbase-request.d.ts +22 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 错误码契约(manager-node · 本仓指引)
|
|
2
|
+
|
|
3
|
+
**规则的唯一来源** → `cloudbase-cli/specs/error-rules.md`(**改规则只改那一处**;本文件只写**本仓差异**)。
|
|
4
|
+
|
|
5
|
+
## 本仓差异
|
|
6
|
+
|
|
7
|
+
- **字段**:三仓应有项全有(含 `docs`、`meta`)+ **本仓专属 `meta.failures`**(多项失败明细登记,见 rules §5)
|
|
8
|
+
- **`docs`**:`this.docs = opts.docs ?? resolveErrorDoc(this.code, this.source)`(**类内派生**,依赖本仓 `src/error-docs.ts`)
|
|
9
|
+
> ⚠️ 映射表**只在本仓并不充分**:TB 侧错误拿不到 `docs` ⇒ 按判据 C 应走**单一来源**(文档站生成)或下沉共享基座;本仓不应成为映射表唯一归属点
|
|
10
|
+
- **机制**:有 `isCloudBaseError` / `isValidErrorCode` / 8 个 helper(含 `throwCredentialError`);**缺 `errorSourceOf`**(R3 前置,待对齐)
|
|
11
|
+
- **守卫**:`test/error.test.ts`(burn-down 平衡括号 + 码表)、`test/error-no-code-baseline.json`(`{}`)
|
|
12
|
+
- **本仓特殊约定**:错误对象是**对外 API**(CLI / MCP / 第三方 SDK 直接读 `.code` / `.docs` / `.meta`)⇒ 字段增删按 §3 冻结形状处理,不随基座版本漂移
|
|
13
|
+
- **新增自造码 ⇒ 必须同步文档站**:到 `cloudbase-docs` 跑 `node scripts/generate-sdk-error-code-pages.mjs`(分层登记页由生成器产出,**勿手工建页**、**勿加进 `basic.md`**;详见该仓 `.codebuddy/skills/error-code-manager`)
|
package/CHANGELOG.md
CHANGED
package/lib/agent/index.js
CHANGED
|
@@ -13,6 +13,7 @@ exports.AgentService = void 0;
|
|
|
13
13
|
const cloudrun_1 = require("../cloudrun");
|
|
14
14
|
const type_1 = require("../cloudrun/type");
|
|
15
15
|
const utils_1 = require("../utils");
|
|
16
|
+
const error_1 = require("../error");
|
|
16
17
|
const path_1 = __importDefault(require("path"));
|
|
17
18
|
/**
|
|
18
19
|
* Agent 管理类
|
|
@@ -254,7 +255,7 @@ class AgentService {
|
|
|
254
255
|
// 1. 查询 Agent 获取类型
|
|
255
256
|
const agentListRes = await this.describeAgentList({ AgentId });
|
|
256
257
|
if (!agentListRes.AgentList || agentListRes.AgentList.length === 0) {
|
|
257
|
-
|
|
258
|
+
(0, error_1.throwNotFound)(`Agent ${AgentId} 不存在`);
|
|
258
259
|
}
|
|
259
260
|
const agentInfo = agentListRes.AgentList[0];
|
|
260
261
|
const agentType = agentInfo.AgentType;
|
|
@@ -273,7 +274,7 @@ class AgentService {
|
|
|
273
274
|
});
|
|
274
275
|
}
|
|
275
276
|
else {
|
|
276
|
-
|
|
277
|
+
(0, error_1.throwInvalidParam)(`不支持的 Agent 类型: ${agentType}`);
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
/**
|
|
@@ -333,7 +334,7 @@ class AgentService {
|
|
|
333
334
|
// 1. 查询 Agent 获取类型
|
|
334
335
|
const agentListRes = await this.describeAgentList({ AgentId });
|
|
335
336
|
if (!agentListRes.AgentList || agentListRes.AgentList.length === 0) {
|
|
336
|
-
|
|
337
|
+
(0, error_1.throwNotFound)(`Agent ${AgentId} 不存在`);
|
|
337
338
|
}
|
|
338
339
|
const agentInfo = agentListRes.AgentList[0];
|
|
339
340
|
const agentType = agentInfo.AgentType;
|
|
@@ -368,7 +369,7 @@ class AgentService {
|
|
|
368
369
|
});
|
|
369
370
|
}
|
|
370
371
|
else {
|
|
371
|
-
|
|
372
|
+
(0, error_1.throwInvalidParam)(`不支持的 Agent 类型: ${agentType}`);
|
|
372
373
|
}
|
|
373
374
|
}
|
|
374
375
|
// ==================== TCBR 云托管(内部方法,暂不对外暴露) ====================
|
|
@@ -592,7 +593,7 @@ class AgentService {
|
|
|
592
593
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
593
594
|
// 判断 params.BotId 的数据格式是否为 ibot-xxx-xxx 的格式
|
|
594
595
|
if (!/^ibot-[a-z0-9_]+-[a-z0-9_]+$/.test(BotId)) {
|
|
595
|
-
|
|
596
|
+
(0, error_1.throwInvalidParam)('BotId格式应为"ibot-xxx-xxx",其中 xxx 仅支持小写字母、数字或下划线');
|
|
596
597
|
}
|
|
597
598
|
// 获取函数型云托管服务的名称:ibot-xxx-xxx 中的 ibot-xxx
|
|
598
599
|
const serverName = BotId.split('-').slice(0, 2).join('-');
|
package/lib/aiModel/index.js
CHANGED
|
@@ -8,6 +8,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.AiModelService = void 0;
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
/** AI 模型分组状态(CreateAIModel / UpdateAIModel 协议值) */
|
|
13
|
+
const MODEL_STATUS = {
|
|
14
|
+
/** 启用 */
|
|
15
|
+
enabled: 1,
|
|
16
|
+
/** 禁用 */
|
|
17
|
+
disabled: 2
|
|
18
|
+
};
|
|
11
19
|
class AiModelService {
|
|
12
20
|
constructor(environment) {
|
|
13
21
|
this.environment = environment;
|
|
@@ -20,13 +28,15 @@ class AiModelService {
|
|
|
20
28
|
const { EnvId } = this.environment.lazyEnvironmentConfig;
|
|
21
29
|
const { envId, groupName, baseUrl, models, remark, status, secret } = options;
|
|
22
30
|
if (!groupName || groupName.trim().length === 0) {
|
|
23
|
-
|
|
31
|
+
(0, error_1.throwInvalidParam)('groupName is required');
|
|
24
32
|
}
|
|
25
33
|
if (groupName.toLowerCase().startsWith('cloudbase')) {
|
|
26
|
-
|
|
34
|
+
(0, error_1.throwInvalidParam)('groupName cannot start with cloudbase');
|
|
27
35
|
}
|
|
28
|
-
if (status !== undefined &&
|
|
29
|
-
|
|
36
|
+
if (status !== undefined &&
|
|
37
|
+
status !== MODEL_STATUS.enabled &&
|
|
38
|
+
status !== MODEL_STATUS.disabled) {
|
|
39
|
+
(0, error_1.throwInvalidParam)(`status must be ${MODEL_STATUS.enabled} or ${MODEL_STATUS.disabled}`);
|
|
30
40
|
}
|
|
31
41
|
return this.tcbService.request('CreateAIModel', Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ EnvId: envId || EnvId, GroupName: groupName }, (baseUrl !== undefined ? { BaseUrl: baseUrl } : {})), (models !== undefined ? { Models: models } : {})), (remark !== undefined ? { Remark: remark } : {})), (status !== undefined ? { Status: status } : {})), (secret !== undefined ? { Secret: secret } : {})));
|
|
32
42
|
}
|
|
@@ -37,10 +47,12 @@ class AiModelService {
|
|
|
37
47
|
const { EnvId } = this.environment.lazyEnvironmentConfig;
|
|
38
48
|
const { envId, groupName, baseUrl, models, remark, status, secret } = options;
|
|
39
49
|
if (!groupName || groupName.trim().length === 0) {
|
|
40
|
-
|
|
50
|
+
(0, error_1.throwInvalidParam)('groupName is required');
|
|
41
51
|
}
|
|
42
|
-
if (status !== undefined &&
|
|
43
|
-
|
|
52
|
+
if (status !== undefined &&
|
|
53
|
+
status !== MODEL_STATUS.enabled &&
|
|
54
|
+
status !== MODEL_STATUS.disabled) {
|
|
55
|
+
(0, error_1.throwInvalidParam)(`status must be ${MODEL_STATUS.enabled} or ${MODEL_STATUS.disabled}`);
|
|
44
56
|
}
|
|
45
57
|
return this.tcbService.request('UpdateAIModel', Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ EnvId: envId || EnvId, GroupName: groupName }, (baseUrl !== undefined ? { BaseUrl: baseUrl } : {})), (models !== undefined ? { Models: models } : {})), (remark !== undefined ? { Remark: remark } : {})), (status !== undefined ? { Status: status } : {})), (secret !== undefined ? { Secret: secret } : {})));
|
|
46
58
|
}
|
|
@@ -51,11 +63,11 @@ class AiModelService {
|
|
|
51
63
|
const { EnvId } = this.environment.lazyEnvironmentConfig;
|
|
52
64
|
const { envId, groupNames } = options;
|
|
53
65
|
if (!Array.isArray(groupNames) || groupNames.length === 0) {
|
|
54
|
-
|
|
66
|
+
(0, error_1.throwInvalidParam)('groupNames is required');
|
|
55
67
|
}
|
|
56
68
|
const invalidGroupName = groupNames.find(item => typeof item !== 'string' || item.trim().length === 0);
|
|
57
69
|
if (invalidGroupName !== undefined) {
|
|
58
|
-
|
|
70
|
+
(0, error_1.throwInvalidParam)('groupNames contains invalid item');
|
|
59
71
|
}
|
|
60
72
|
return this.tcbService.request('DeleteAIModel', {
|
|
61
73
|
EnvId: envId || EnvId,
|
package/lib/cloudApp/index.js
CHANGED
|
@@ -133,11 +133,11 @@ class CloudAppService {
|
|
|
133
133
|
const { serviceName, localPath, ignore = [], excludeNodeModules = true, onPhase } = params;
|
|
134
134
|
// 1. 验证本地路径
|
|
135
135
|
if (!fs_1.default.existsSync(localPath)) {
|
|
136
|
-
|
|
136
|
+
(0, error_1.throwNotFound)(`本地路径不存在: ${localPath}`);
|
|
137
137
|
}
|
|
138
138
|
const stat = fs_1.default.statSync(localPath);
|
|
139
139
|
if (!stat.isDirectory()) {
|
|
140
|
-
|
|
140
|
+
(0, error_1.throwInvalidParam)(`localPath 必须是目录: ${localPath}`);
|
|
141
141
|
}
|
|
142
142
|
// 2. 打包本地文件为 ZIP(node_modules 按场景决定是否排除)
|
|
143
143
|
const tmpZipPath = path_1.default.join(os_1.default.tmpdir(), `cloudapp-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);
|
|
@@ -193,7 +193,7 @@ class CloudAppService {
|
|
|
193
193
|
catch (_a) {
|
|
194
194
|
// 解析失败时 errorDetail 保持空串,不影响主错误抛出
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
(0, error_1.throwApiError)(`上传失败: ${response.status} ${response.statusText}${errorDetail}`);
|
|
197
197
|
}
|
|
198
198
|
onPhase === null || onPhase === void 0 ? void 0 : onPhase({ phase: 'upload', durationMs: Date.now() - putStart, bytes: zipBuffer.length });
|
|
199
199
|
// 7. 返回结果(返回 cosTimestamp,用于创建应用)
|
|
@@ -226,7 +226,7 @@ class CloudAppService {
|
|
|
226
226
|
validateRequiredParams(params, fields) {
|
|
227
227
|
for (const field of fields) {
|
|
228
228
|
if (!params[field]) {
|
|
229
|
-
|
|
229
|
+
(0, error_1.throwInvalidParam)(`${field} 是必填参数`);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
}
|
package/lib/cloudrun/index.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.parseObjectToDiffConfigItem = parseObjectToDiffConfigItem;
|
|
|
15
15
|
const archiver_1 = __importDefault(require("archiver"));
|
|
16
16
|
const fs_extra_1 = require("fs-extra");
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const error_1 = require("../error");
|
|
18
19
|
const utils_1 = require("../utils");
|
|
19
20
|
const type_1 = require("./type");
|
|
20
21
|
/**
|
|
@@ -46,7 +47,7 @@ class CloudRunService {
|
|
|
46
47
|
const templates = await this.getTemplates();
|
|
47
48
|
const templateData = templates.find(item => item.identifier === template);
|
|
48
49
|
if (!templateData || !templateData.zipFileStore) {
|
|
49
|
-
|
|
50
|
+
(0, error_1.throwNotFound)(`未找到匹配的模板: ${template}`);
|
|
50
51
|
}
|
|
51
52
|
// 2. 下载并解压模板到目标目录
|
|
52
53
|
const downloadUrl = templateData.zipFileStore;
|
|
@@ -75,7 +76,7 @@ class CloudRunService {
|
|
|
75
76
|
const cloudRunServerDetailRes = await this.detail({ serverName });
|
|
76
77
|
const version = (_b = (_a = cloudRunServerDetailRes.OnlineVersionInfos) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.VersionName;
|
|
77
78
|
if (!version) {
|
|
78
|
-
|
|
79
|
+
(0, error_1.throwNotFound)('未找到云托管服务版本');
|
|
79
80
|
}
|
|
80
81
|
/**
|
|
81
82
|
* 获取下载地址
|
|
@@ -89,7 +90,7 @@ class CloudRunService {
|
|
|
89
90
|
await (0, utils_1.downloadAndExtractRemoteZip)(cloudBaseBuildServiceRes === null || cloudBaseBuildServiceRes === void 0 ? void 0 : cloudBaseBuildServiceRes.DownloadUrl, path_1.default.resolve(targetPath));
|
|
90
91
|
}
|
|
91
92
|
else {
|
|
92
|
-
|
|
93
|
+
(0, error_1.throwApiError)(`云托管代码下载地址为空(请求ID: ${cloudBaseBuildServiceRes.RequestId})`);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
@@ -116,7 +117,7 @@ class CloudRunService {
|
|
|
116
117
|
const serverName = (_a = params === null || params === void 0 ? void 0 : params.ServerName) === null || _a === void 0 ? void 0 : _a.trim();
|
|
117
118
|
const status = (_b = params === null || params === void 0 ? void 0 : params.Status) === null || _b === void 0 ? void 0 : _b.trim();
|
|
118
119
|
if (!serverName) {
|
|
119
|
-
|
|
120
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: ServerName');
|
|
120
121
|
}
|
|
121
122
|
return this.tcbrService.request('DescribeReleaseOrder', Object.assign({ EnvId: envConfig.EnvId, ServerName: serverName }, (status ? { Status: status } : {})));
|
|
122
123
|
}
|
|
@@ -148,7 +149,7 @@ class CloudRunService {
|
|
|
148
149
|
async setTraffic(serverName, stablePercent, canaryPercent) {
|
|
149
150
|
// 校验比例之和是否为100%
|
|
150
151
|
if (stablePercent + canaryPercent !== 100) {
|
|
151
|
-
|
|
152
|
+
(0, error_1.throwInvalidParam)('稳定版本流量比例和灰度版本流量比例之和必须等于 100');
|
|
152
153
|
}
|
|
153
154
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
154
155
|
const { Task } = await this.tcbrService.request('DescribeServerManageTask', {
|
|
@@ -159,7 +160,7 @@ class CloudRunService {
|
|
|
159
160
|
// 判断是否存在灰度版本
|
|
160
161
|
const isGary = (Task === null || Task === void 0 ? void 0 : Task.ReleaseType) === type_1.ReleaseTypeEnum.GRAY && (Task === null || Task === void 0 ? void 0 : Task.Status) === 'running';
|
|
161
162
|
if (!isGary) {
|
|
162
|
-
|
|
163
|
+
(0, error_1.throwApiError)('不存在灰度中的版本或灰度版本部署未完成');
|
|
163
164
|
}
|
|
164
165
|
// 获取当前版本和灰度版本
|
|
165
166
|
const { ReleaseOrderInfo } = await this.tcbrService.request('DescribeReleaseOrder', {
|
|
@@ -242,13 +243,13 @@ class CloudRunService {
|
|
|
242
243
|
const rollbackVersionName = (_c = params === null || params === void 0 ? void 0 : params.RollbackVersionName) === null || _c === void 0 ? void 0 : _c.trim();
|
|
243
244
|
const operatorRemark = (_d = params === null || params === void 0 ? void 0 : params.OperatorRemark) === null || _d === void 0 ? void 0 : _d.trim();
|
|
244
245
|
if (!serverName) {
|
|
245
|
-
|
|
246
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: ServerName');
|
|
246
247
|
}
|
|
247
248
|
if (!currentVersionName) {
|
|
248
|
-
|
|
249
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: CurrentVersionName');
|
|
249
250
|
}
|
|
250
251
|
if (!rollbackVersionName) {
|
|
251
|
-
|
|
252
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: RollbackVersionName');
|
|
252
253
|
}
|
|
253
254
|
return this.tcbrService.request('SubmitServerRollback', Object.assign({ EnvId: envConfig.EnvId, ServerName: serverName, CurrentVersionName: currentVersionName, RollbackVersionName: rollbackVersionName }, (operatorRemark ? { OperatorRemark: operatorRemark } : {})));
|
|
254
255
|
}
|
|
@@ -262,17 +263,17 @@ class CloudRunService {
|
|
|
262
263
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
263
264
|
const operatorRemark = (_a = params === null || params === void 0 ? void 0 : params.OperatorRemark) === null || _a === void 0 ? void 0 : _a.trim();
|
|
264
265
|
if (!Array.isArray(params === null || params === void 0 ? void 0 : params.SimpleVersions) || params.SimpleVersions.length === 0) {
|
|
265
|
-
|
|
266
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: SimpleVersions');
|
|
266
267
|
}
|
|
267
268
|
const simpleVersions = params.SimpleVersions.map((item, index) => {
|
|
268
269
|
var _a, _b, _c;
|
|
269
270
|
const serverName = (_a = item === null || item === void 0 ? void 0 : item.ServerName) === null || _a === void 0 ? void 0 : _a.trim();
|
|
270
271
|
const versionName = (_b = item === null || item === void 0 ? void 0 : item.VersionName) === null || _b === void 0 ? void 0 : _b.trim();
|
|
271
272
|
if (!serverName) {
|
|
272
|
-
|
|
273
|
+
(0, error_1.throwInvalidParam)(`Invalid SimpleVersions[${index}].ServerName`);
|
|
273
274
|
}
|
|
274
275
|
if (!versionName) {
|
|
275
|
-
|
|
276
|
+
(0, error_1.throwInvalidParam)(`Invalid SimpleVersions[${index}].VersionName`);
|
|
276
277
|
}
|
|
277
278
|
return {
|
|
278
279
|
EnvId: ((_c = item === null || item === void 0 ? void 0 : item.EnvId) === null || _c === void 0 ? void 0 : _c.trim()) || envConfig.EnvId,
|
|
@@ -294,10 +295,10 @@ class CloudRunService {
|
|
|
294
295
|
const versionName = (_b = params === null || params === void 0 ? void 0 : params.VersionName) === null || _b === void 0 ? void 0 : _b.trim();
|
|
295
296
|
const channel = (_c = params === null || params === void 0 ? void 0 : params.Channel) === null || _c === void 0 ? void 0 : _c.trim();
|
|
296
297
|
if (!serverName) {
|
|
297
|
-
|
|
298
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: ServerName');
|
|
298
299
|
}
|
|
299
300
|
if (!versionName) {
|
|
300
|
-
|
|
301
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: VersionName');
|
|
301
302
|
}
|
|
302
303
|
return this.tcbrService.request('DescribeVersionDetail', Object.assign({ EnvId: envConfig.EnvId, ServerName: serverName, VersionName: versionName }, (channel ? { Channel: channel } : {})));
|
|
303
304
|
}
|
|
@@ -359,7 +360,7 @@ class CloudRunService {
|
|
|
359
360
|
* 参数校验和默认值设置
|
|
360
361
|
*/
|
|
361
362
|
if (!serverName) {
|
|
362
|
-
|
|
363
|
+
(0, error_1.throwInvalidParam)('Missing required parameters: serviceName');
|
|
363
364
|
}
|
|
364
365
|
// 获取当前环境配置(包含EnvId)
|
|
365
366
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
package/lib/common/index.js
CHANGED
|
@@ -30,7 +30,7 @@ class CommonService {
|
|
|
30
30
|
async call(options) {
|
|
31
31
|
const { Action, Param = {} } = options;
|
|
32
32
|
if (!Action) {
|
|
33
|
-
|
|
33
|
+
(0, error_1.throwInvalidParam)('缺少必填参数 Action');
|
|
34
34
|
}
|
|
35
35
|
const res = await this.commonService.request(Action, Object.assign({}, Param));
|
|
36
36
|
return res;
|
package/lib/database/index.js
CHANGED
|
@@ -201,7 +201,7 @@ class DatabaseService {
|
|
|
201
201
|
fileType = path_1.default.extname(filePath).substring(1);
|
|
202
202
|
}
|
|
203
203
|
else {
|
|
204
|
-
|
|
204
|
+
(0, error_1.throwInvalidParam)('Miss file.filePath or file.objectKey');
|
|
205
205
|
}
|
|
206
206
|
if (file['FileType']) {
|
|
207
207
|
fileType = file['FileType'];
|
|
@@ -217,7 +217,7 @@ class DatabaseService {
|
|
|
217
217
|
fileType = path_1.default.extname(filePath).substring(1);
|
|
218
218
|
}
|
|
219
219
|
else {
|
|
220
|
-
|
|
220
|
+
(0, error_1.throwInvalidParam)('Miss file.filePath or file.objectKey');
|
|
221
221
|
}
|
|
222
222
|
if (file['FileType']) {
|
|
223
223
|
fileType = file['FileType'];
|
|
@@ -334,17 +334,17 @@ class DatabaseService {
|
|
|
334
334
|
var _a;
|
|
335
335
|
const { EnvId, Migrations, IncludeAll } = options;
|
|
336
336
|
if (!Array.isArray(Migrations) || Migrations.length === 0) {
|
|
337
|
-
|
|
337
|
+
(0, error_1.throwInvalidParam)('Migrations 至少需要 1 条');
|
|
338
338
|
}
|
|
339
339
|
for (const migration of Migrations) {
|
|
340
340
|
if (!/^\d{14}$/.test(migration.Version)) {
|
|
341
|
-
|
|
341
|
+
(0, error_1.throwInvalidParam)('Migration.Version 必须是 14 位数字');
|
|
342
342
|
}
|
|
343
343
|
if (!/^[a-z_]+$/.test(migration.Name)) {
|
|
344
|
-
|
|
344
|
+
(0, error_1.throwInvalidParam)('Migration.Name 只允许小写字母和下划线');
|
|
345
345
|
}
|
|
346
346
|
if (!((_a = migration.Query) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
347
|
-
|
|
347
|
+
(0, error_1.throwInvalidParam)('Migration.Query 不能为空');
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
350
|
return this.dbOpService.request('PreviewPGUserMigrations', Object.assign({ EnvId: EnvId || this.envId, Migrations }, (IncludeAll !== undefined ? { IncludeAll } : {})));
|
|
@@ -358,24 +358,24 @@ class DatabaseService {
|
|
|
358
358
|
var _a;
|
|
359
359
|
const { EnvId, Migrations, LockTimeoutMs, StatementTimeoutMs, IncludeAll } = options;
|
|
360
360
|
if (!Array.isArray(Migrations) || Migrations.length === 0) {
|
|
361
|
-
|
|
361
|
+
(0, error_1.throwInvalidParam)('Migrations 至少需要 1 条');
|
|
362
362
|
}
|
|
363
363
|
for (const migration of Migrations) {
|
|
364
364
|
if (!/^\d{14}$/.test(migration.Version)) {
|
|
365
|
-
|
|
365
|
+
(0, error_1.throwInvalidParam)('Migration.Version 必须是 14 位数字');
|
|
366
366
|
}
|
|
367
367
|
if (!/^[a-z_]+$/.test(migration.Name)) {
|
|
368
|
-
|
|
368
|
+
(0, error_1.throwInvalidParam)('Migration.Name 只允许小写字母和下划线');
|
|
369
369
|
}
|
|
370
370
|
if (!((_a = migration.Query) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
371
|
-
|
|
371
|
+
(0, error_1.throwInvalidParam)('Migration.Query 不能为空');
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
if (LockTimeoutMs !== undefined && LockTimeoutMs < 0) {
|
|
375
|
-
|
|
375
|
+
(0, error_1.throwInvalidParam)('LockTimeoutMs 不能小于 0');
|
|
376
376
|
}
|
|
377
377
|
if (StatementTimeoutMs !== undefined && StatementTimeoutMs < 0) {
|
|
378
|
-
|
|
378
|
+
(0, error_1.throwInvalidParam)('StatementTimeoutMs 不能小于 0');
|
|
379
379
|
}
|
|
380
380
|
return this.dbOpService.request('PushPGUserMigrations', Object.assign(Object.assign(Object.assign({ EnvId: EnvId || this.envId, Migrations }, (LockTimeoutMs !== undefined ? { LockTimeoutMs } : {})), (StatementTimeoutMs !== undefined ? { StatementTimeoutMs } : {})), (IncludeAll !== undefined ? { IncludeAll } : {})));
|
|
381
381
|
}
|
|
@@ -387,19 +387,19 @@ class DatabaseService {
|
|
|
387
387
|
async repairPGUserMigrationHistory(options) {
|
|
388
388
|
const { EnvId, MigrationVersion, Name, Status, Reason, Query } = options;
|
|
389
389
|
if (!/^\d{14}$/.test(MigrationVersion)) {
|
|
390
|
-
|
|
390
|
+
(0, error_1.throwInvalidParam)('MigrationVersion 必须是 14 位数字');
|
|
391
391
|
}
|
|
392
392
|
if (!/^[a-z_]+$/.test(Name)) {
|
|
393
|
-
|
|
393
|
+
(0, error_1.throwInvalidParam)('Name 只允许小写字母和下划线');
|
|
394
394
|
}
|
|
395
395
|
if (Status !== 'applied' && Status !== 'reverted') {
|
|
396
|
-
|
|
396
|
+
(0, error_1.throwInvalidParam)('Status 只允许 applied 或 reverted');
|
|
397
397
|
}
|
|
398
398
|
if (!(Reason === null || Reason === void 0 ? void 0 : Reason.trim())) {
|
|
399
|
-
|
|
399
|
+
(0, error_1.throwInvalidParam)('Reason 不能为空');
|
|
400
400
|
}
|
|
401
401
|
if (Status === 'applied' && !(Query === null || Query === void 0 ? void 0 : Query.trim())) {
|
|
402
|
-
|
|
402
|
+
(0, error_1.throwInvalidParam)('Status=applied 时 Query 不能为空');
|
|
403
403
|
}
|
|
404
404
|
const params = {
|
|
405
405
|
EnvId: EnvId || this.envId,
|
|
@@ -421,10 +421,10 @@ class DatabaseService {
|
|
|
421
421
|
async listPGUserMigrations(options = {}) {
|
|
422
422
|
const { EnvId, Limit, Offset } = options;
|
|
423
423
|
if (Limit !== undefined && (Limit < 1 || Limit > 500)) {
|
|
424
|
-
|
|
424
|
+
(0, error_1.throwInvalidParam)('Limit 取值范围为 [1, 500]');
|
|
425
425
|
}
|
|
426
426
|
if (Offset !== undefined && Offset < 0) {
|
|
427
|
-
|
|
427
|
+
(0, error_1.throwInvalidParam)('Offset 不能小于 0');
|
|
428
428
|
}
|
|
429
429
|
return this.dbOpService.request('ListPGUserMigrations', Object.assign(Object.assign({ EnvId: EnvId || this.envId }, (Limit !== undefined ? { Limit } : {})), (Offset !== undefined ? { Offset } : {})));
|
|
430
430
|
}
|
|
@@ -436,7 +436,7 @@ class DatabaseService {
|
|
|
436
436
|
async listAllPGUserMigrations(options = {}) {
|
|
437
437
|
const { EnvId, PageSize = 500 } = options;
|
|
438
438
|
if (PageSize < 1 || PageSize > 500) {
|
|
439
|
-
|
|
439
|
+
(0, error_1.throwInvalidParam)('PageSize 取值范围为 [1, 500]');
|
|
440
440
|
}
|
|
441
441
|
const migrations = [];
|
|
442
442
|
let offset = 0;
|
|
@@ -474,7 +474,7 @@ class DatabaseService {
|
|
|
474
474
|
async describePGUserMigration(options) {
|
|
475
475
|
const { EnvId, MigrationVersion } = options;
|
|
476
476
|
if (!/^\d{14}$/.test(MigrationVersion)) {
|
|
477
|
-
|
|
477
|
+
(0, error_1.throwInvalidParam)('MigrationVersion 必须是 14 位数字');
|
|
478
478
|
}
|
|
479
479
|
return this.dbOpService.request('DescribePGUserMigration', {
|
|
480
480
|
EnvId: EnvId || this.envId,
|
|
@@ -489,7 +489,7 @@ class DatabaseService {
|
|
|
489
489
|
async describeTaskResult(options) {
|
|
490
490
|
const { EnvId, TaskId } = options;
|
|
491
491
|
if (!(TaskId === null || TaskId === void 0 ? void 0 : TaskId.trim())) {
|
|
492
|
-
|
|
492
|
+
(0, error_1.throwInvalidParam)('TaskId 不能为空');
|
|
493
493
|
}
|
|
494
494
|
return this.dbOpService.request('DescribeTaskResult', {
|
|
495
495
|
EnvId: EnvId || this.envId,
|
|
@@ -501,7 +501,7 @@ class DatabaseService {
|
|
|
501
501
|
const currEnv = this.environment;
|
|
502
502
|
const instanceId = (_c = (_b = (_a = currEnv.lazyEnvironmentConfig) === null || _a === void 0 ? void 0 : _a.Databases) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.InstanceId;
|
|
503
503
|
if (!instanceId) {
|
|
504
|
-
|
|
504
|
+
(0, error_1.throwApiError)('当前环境未开通文档型数据库');
|
|
505
505
|
}
|
|
506
506
|
return instanceId;
|
|
507
507
|
}
|
|
@@ -27,7 +27,7 @@ class DatabaseDeployer {
|
|
|
27
27
|
*/
|
|
28
28
|
parseMigrationFiles(migrationsDir) {
|
|
29
29
|
if (!fs_1.default.existsSync(migrationsDir)) {
|
|
30
|
-
|
|
30
|
+
(0, error_1.throwNotFound)(`数据库迁移目录不存在: ${migrationsDir}`);
|
|
31
31
|
}
|
|
32
32
|
const files = fs_1.default.readdirSync(migrationsDir)
|
|
33
33
|
.filter(f => f.endsWith('.sql'))
|
|
@@ -36,13 +36,13 @@ class DatabaseDeployer {
|
|
|
36
36
|
for (const file of files) {
|
|
37
37
|
const match = file.match(MIGRATION_FILE_PATTERN);
|
|
38
38
|
if (!match) {
|
|
39
|
-
|
|
39
|
+
(0, error_1.throwInvalidParam)(`migration 文件名格式错误: ${file},应为 {14位时间戳}_{小写下划线名称}.sql`);
|
|
40
40
|
}
|
|
41
41
|
const [, version, name] = match;
|
|
42
42
|
const filePath = path_1.default.join(migrationsDir, file);
|
|
43
43
|
const query = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
44
44
|
if (!query.trim()) {
|
|
45
|
-
|
|
45
|
+
(0, error_1.throwInvalidParam)(`migration 文件内容为空: ${file}`);
|
|
46
46
|
}
|
|
47
47
|
migrations.push({ Version: version, Name: name, Query: query });
|
|
48
48
|
}
|
|
@@ -71,10 +71,9 @@ class DatabaseDeployer {
|
|
|
71
71
|
}
|
|
72
72
|
catch (error) {
|
|
73
73
|
const err = error;
|
|
74
|
-
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
74
|
+
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, { code: 'API_ERROR', source: 'manager-node',
|
|
75
75
|
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
76
|
-
original: err instanceof Error ? err : undefined
|
|
77
|
-
});
|
|
76
|
+
original: err instanceof Error ? err : undefined });
|
|
78
77
|
}
|
|
79
78
|
const items = [];
|
|
80
79
|
// pending:将要执行的
|
|
@@ -124,16 +123,15 @@ class DatabaseDeployer {
|
|
|
124
123
|
}
|
|
125
124
|
catch (error) {
|
|
126
125
|
const err = error;
|
|
127
|
-
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
126
|
+
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, { code: 'API_ERROR', source: 'manager-node',
|
|
128
127
|
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
129
|
-
original: err instanceof Error ? err : undefined
|
|
130
|
-
});
|
|
128
|
+
original: err instanceof Error ? err : undefined });
|
|
131
129
|
}
|
|
132
130
|
if (!preview.Executable) {
|
|
133
131
|
const conflictDetails = (preview.Conflicts || [])
|
|
134
132
|
.map((c) => ` - ${c.Version}_${c.Name || '?'}: ${c.Reason || c.Message || 'unknown'}`)
|
|
135
133
|
.join('\n');
|
|
136
|
-
throw new error_1.CloudBaseError(`数据库迁移存在冲突,中断部署。请先解决以下冲突:\n${conflictDetails}`, {
|
|
134
|
+
throw new error_1.CloudBaseError(`数据库迁移存在冲突,中断部署。请先解决以下冲突:\n${conflictDetails}`, { code: 'API_ERROR', source: 'manager-node',
|
|
137
135
|
requestId: preview.RequestId
|
|
138
136
|
});
|
|
139
137
|
}
|
|
@@ -151,24 +149,22 @@ class DatabaseDeployer {
|
|
|
151
149
|
}
|
|
152
150
|
catch (error) {
|
|
153
151
|
const err = error;
|
|
154
|
-
throw new error_1.CloudBaseError(`数据库迁移提交失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
152
|
+
throw new error_1.CloudBaseError(`数据库迁移提交失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, { code: 'API_ERROR', source: 'manager-node',
|
|
155
153
|
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
156
|
-
original: err instanceof Error ? err : undefined
|
|
157
|
-
});
|
|
154
|
+
original: err instanceof Error ? err : undefined });
|
|
158
155
|
}
|
|
159
156
|
// 4. 轮询等待完成
|
|
160
157
|
try {
|
|
161
158
|
await this.waitForTask(envId, result.TaskId, DEFAULT_TIMEOUT_MS);
|
|
162
159
|
}
|
|
163
160
|
catch (error) {
|
|
164
|
-
if (
|
|
161
|
+
if ((0, error_1.isCloudBaseError)(error)) {
|
|
165
162
|
throw error;
|
|
166
163
|
}
|
|
167
164
|
const err = error;
|
|
168
|
-
throw new error_1.CloudBaseError(`数据库迁移任务等待失败 [TaskId=${result.TaskId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
165
|
+
throw new error_1.CloudBaseError(`数据库迁移任务等待失败 [TaskId=${result.TaskId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, { code: 'API_ERROR', source: 'manager-node',
|
|
169
166
|
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
170
|
-
original: err instanceof Error ? err : undefined
|
|
171
|
-
});
|
|
167
|
+
original: err instanceof Error ? err : undefined });
|
|
172
168
|
}
|
|
173
169
|
return { taskId: result.TaskId };
|
|
174
170
|
}
|
|
@@ -180,7 +176,7 @@ class DatabaseDeployer {
|
|
|
180
176
|
resolveMigrationsDir(cwd, migrations) {
|
|
181
177
|
const raw = migrations || './cloudbase/migrations';
|
|
182
178
|
if (path_1.default.isAbsolute(raw) || raw.startsWith('/')) {
|
|
183
|
-
|
|
179
|
+
(0, error_1.throwInvalidParam)('database.migrations 仅支持相对路径(例如 ./cloudbase/migrations)');
|
|
184
180
|
}
|
|
185
181
|
return path_1.default.resolve(cwd, raw);
|
|
186
182
|
}
|
|
@@ -189,7 +185,7 @@ class DatabaseDeployer {
|
|
|
189
185
|
*/
|
|
190
186
|
assertDatabaseType(config) {
|
|
191
187
|
if (config.type !== 'postgresql') {
|
|
192
|
-
|
|
188
|
+
(0, error_1.throwInvalidParam)(`database.type 仅支持 postgresql,当前为: ${String(config.type)}`);
|
|
193
189
|
}
|
|
194
190
|
}
|
|
195
191
|
/**
|
|
@@ -206,10 +202,9 @@ class DatabaseDeployer {
|
|
|
206
202
|
catch (error) {
|
|
207
203
|
if (Date.now() - startTime > timeoutMs) {
|
|
208
204
|
const err = error;
|
|
209
|
-
throw new error_1.CloudBaseError(`数据库迁移任务状态查询超时(${timeoutMs / 1000}s),TaskId: ${taskId},最后错误:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
205
|
+
throw new error_1.CloudBaseError(`数据库迁移任务状态查询超时(${timeoutMs / 1000}s),TaskId: ${taskId},最后错误:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, { code: 'NETWORK_ERROR', source: 'manager-node',
|
|
210
206
|
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
211
|
-
original: err instanceof Error ? err : undefined
|
|
212
|
-
});
|
|
207
|
+
original: err instanceof Error ? err : undefined });
|
|
213
208
|
}
|
|
214
209
|
await new Promise(resolve => {
|
|
215
210
|
setTimeout(resolve, POLL_INTERVAL_MS);
|
|
@@ -220,12 +215,12 @@ class DatabaseDeployer {
|
|
|
220
215
|
return;
|
|
221
216
|
}
|
|
222
217
|
if (task.Status === 'Failed') {
|
|
223
|
-
throw new error_1.CloudBaseError(`数据库迁移任务失败 [TaskId=${taskId}]: ${task.Reason || '未知原因'}`, {
|
|
218
|
+
throw new error_1.CloudBaseError(`数据库迁移任务失败 [TaskId=${taskId}]: ${task.Reason || '未知原因'}`, { code: 'API_ERROR', source: 'manager-node',
|
|
224
219
|
requestId: task.RequestId
|
|
225
220
|
});
|
|
226
221
|
}
|
|
227
222
|
if (Date.now() - startTime > timeoutMs) {
|
|
228
|
-
throw new error_1.CloudBaseError(`数据库迁移任务超时(${timeoutMs / 1000}s),TaskId: ${taskId},当前状态: ${task.Status}/${task.Phase}`, {
|
|
223
|
+
throw new error_1.CloudBaseError(`数据库迁移任务超时(${timeoutMs / 1000}s),TaskId: ${taskId},当前状态: ${task.Status}/${task.Phase}`, { code: 'NETWORK_ERROR', source: 'manager-node',
|
|
229
224
|
requestId: task.RequestId
|
|
230
225
|
});
|
|
231
226
|
}
|
|
@@ -531,10 +531,10 @@ class DeployOrchestrator {
|
|
|
531
531
|
*/
|
|
532
532
|
guardConfig(config) {
|
|
533
533
|
if (!config || typeof config !== 'object') {
|
|
534
|
-
|
|
534
|
+
(0, error_1.throwInvalidParam)('无效的配置:config 必须为对象');
|
|
535
535
|
}
|
|
536
536
|
if (!config.envId) {
|
|
537
|
-
|
|
537
|
+
(0, error_1.throwInvalidParam)('无效的配置:缺少 envId');
|
|
538
538
|
}
|
|
539
539
|
}
|
|
540
540
|
}
|
|
@@ -447,6 +447,16 @@ class FunctionDeployer {
|
|
|
447
447
|
return;
|
|
448
448
|
}
|
|
449
449
|
const failures = report.checks.filter(item => item.status === 'fail');
|
|
450
|
+
// ⚠️ `report.ready === false` 但没有任何 `status === 'fail'` 的检查项(报告不自洽)时,
|
|
451
|
+
// `failures` 是空数组,直接取 `failures[0].id` 会先抛 TypeError、把真正的校验失败盖掉
|
|
452
|
+
// (代码评审:边界条件)。这里合成一条兜底失败项,保证 code / message / meta.failures 都有取值。
|
|
453
|
+
if (failures.length === 0) {
|
|
454
|
+
failures.push({
|
|
455
|
+
id: 'CHECK_REPORT_INCONSISTENT',
|
|
456
|
+
status: 'fail',
|
|
457
|
+
summary: '部署前检查未通过,但未给出具体失败项,请重试或用 --debug 查看完整报告'
|
|
458
|
+
});
|
|
459
|
+
}
|
|
450
460
|
const message = failures
|
|
451
461
|
.map(item => {
|
|
452
462
|
const remediation = item.remediation ? `,${item.remediation}` : '';
|
|
@@ -454,7 +464,15 @@ class FunctionDeployer {
|
|
|
454
464
|
})
|
|
455
465
|
.join('\n');
|
|
456
466
|
throw new error_1.CloudBaseError(`部署前检查未通过:\n${message}`, {
|
|
457
|
-
code: failures[0].id
|
|
467
|
+
code: failures[0].id,
|
|
468
|
+
// code 只能承载第一个失败项,meta.failures 保留全量(埋点才能统计真实分布)
|
|
469
|
+
meta: {
|
|
470
|
+
failures: failures.map(item => ({
|
|
471
|
+
code: item.id,
|
|
472
|
+
summary: item.summary,
|
|
473
|
+
remediation: item.remediation
|
|
474
|
+
}))
|
|
475
|
+
}
|
|
458
476
|
});
|
|
459
477
|
}
|
|
460
478
|
/**
|