@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
package/lib/storage/index.js
CHANGED
|
@@ -114,7 +114,7 @@ class StorageService {
|
|
|
114
114
|
const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
|
|
115
115
|
const firstError = results.find(isError);
|
|
116
116
|
if (firstError) {
|
|
117
|
-
|
|
117
|
+
(0, error_1.throwFileError)(`计算本地文件摘要失败:${firstError.message}`);
|
|
118
118
|
}
|
|
119
119
|
return results.filter(res => res !== null);
|
|
120
120
|
}
|
|
@@ -213,7 +213,7 @@ class StorageService {
|
|
|
213
213
|
CopySource: copySource
|
|
214
214
|
});
|
|
215
215
|
if (res.statusCode !== 200) {
|
|
216
|
-
|
|
216
|
+
(0, error_1.throwApiError)(`备份文件失败:${file.key}`);
|
|
217
217
|
}
|
|
218
218
|
return destPath;
|
|
219
219
|
}));
|
|
@@ -221,7 +221,7 @@ class StorageService {
|
|
|
221
221
|
const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
|
|
222
222
|
const firstError = results.find(isError);
|
|
223
223
|
if (firstError) {
|
|
224
|
-
|
|
224
|
+
(0, error_1.throwApiError)(`远端备份失败:${firstError.message}`);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
/**
|
|
@@ -246,7 +246,7 @@ class StorageService {
|
|
|
246
246
|
CopySource: copySource
|
|
247
247
|
});
|
|
248
248
|
if (res.statusCode !== 200) {
|
|
249
|
-
|
|
249
|
+
(0, error_1.throwApiError)(`恢复备份文件失败:${key}`);
|
|
250
250
|
}
|
|
251
251
|
return key;
|
|
252
252
|
}));
|
|
@@ -254,7 +254,7 @@ class StorageService {
|
|
|
254
254
|
const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
|
|
255
255
|
const firstRestoreError = restoreResults.find(isError);
|
|
256
256
|
if (firstRestoreError) {
|
|
257
|
-
|
|
257
|
+
(0, error_1.throwApiError)(`从备份恢复远端文件失败:${firstRestoreError.message}`);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
if (addedKeys.length > 0) {
|
|
@@ -418,7 +418,7 @@ class StorageService {
|
|
|
418
418
|
});
|
|
419
419
|
const verifyResult = this.verifyManifest(localManifest, remoteManifest);
|
|
420
420
|
if (!verifyResult.ok) {
|
|
421
|
-
|
|
421
|
+
(0, error_1.throwApiError)(`一致性校验失败:missing=${verifyResult.missing.join(',') || '[]'}; mismatched=${verifyResult.mismatched.join(',') || '[]'}`);
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
}
|
|
@@ -439,9 +439,9 @@ class StorageService {
|
|
|
439
439
|
});
|
|
440
440
|
}
|
|
441
441
|
catch (rollbackError) {
|
|
442
|
-
|
|
442
|
+
(0, error_1.throwApiError)(`一致性发布失败,且回滚失败,请使用备份人工恢复:${backupPrefix};原始错误:${error.message};回滚错误:${rollbackError.message}`);
|
|
443
443
|
}
|
|
444
|
-
|
|
444
|
+
(0, error_1.throwApiError)(`一致性发布失败,已自动回滚,备份保留于 ${backupPrefix},确认无误后可调用 cleanupBackup 清理:${error.message}`);
|
|
445
445
|
}
|
|
446
446
|
// 上传与可选校验均成功后才允许清理远端冗余文件。
|
|
447
447
|
// 删除失败会记录在结果中,不触发已经成功发布内容的回滚。
|
|
@@ -530,7 +530,7 @@ class StorageService {
|
|
|
530
530
|
localFilePath = resolveLocalPath;
|
|
531
531
|
}
|
|
532
532
|
if (!localFilePath) {
|
|
533
|
-
|
|
533
|
+
(0, error_1.throwNotFound)('本地文件不存在!');
|
|
534
534
|
}
|
|
535
535
|
const cos = this.getCos();
|
|
536
536
|
const putObject = util_1.default.promisify(cos.putObject).bind(cos);
|
|
@@ -570,7 +570,7 @@ class StorageService {
|
|
|
570
570
|
});
|
|
571
571
|
}
|
|
572
572
|
if (res.statusCode !== 200) {
|
|
573
|
-
|
|
573
|
+
(0, error_1.throwApiError)(`上传文件错误:${JSON.stringify(res)}`);
|
|
574
574
|
}
|
|
575
575
|
return res;
|
|
576
576
|
}
|
|
@@ -1015,7 +1015,7 @@ class StorageService {
|
|
|
1015
1015
|
Body: ''
|
|
1016
1016
|
});
|
|
1017
1017
|
if (res.statusCode !== 200) {
|
|
1018
|
-
|
|
1018
|
+
(0, error_1.throwApiError)(`创建文件夹失败:${JSON.stringify(res)}`);
|
|
1019
1019
|
}
|
|
1020
1020
|
}
|
|
1021
1021
|
/**
|
|
@@ -1097,7 +1097,7 @@ class StorageService {
|
|
|
1097
1097
|
*/
|
|
1098
1098
|
async getTemporaryUrl(fileList) {
|
|
1099
1099
|
if (!fileList || !Array.isArray(fileList)) {
|
|
1100
|
-
|
|
1100
|
+
(0, error_1.throwInvalidParam)('fileList 必须是非空的数组');
|
|
1101
1101
|
}
|
|
1102
1102
|
const files = fileList.map(item => {
|
|
1103
1103
|
if (typeof item === 'string') {
|
|
@@ -1109,7 +1109,7 @@ class StorageService {
|
|
|
1109
1109
|
});
|
|
1110
1110
|
const invalidData = files.find(item => !item.cloudPath || !item.maxAge || typeof item.cloudPath !== 'string');
|
|
1111
1111
|
if (invalidData) {
|
|
1112
|
-
|
|
1112
|
+
(0, error_1.throwInvalidParam)(`非法参数:${JSON.stringify(invalidData)}`);
|
|
1113
1113
|
}
|
|
1114
1114
|
const notExistsFiles = [];
|
|
1115
1115
|
const checkFileRequests = files.map(file => (async () => {
|
|
@@ -1125,7 +1125,7 @@ class StorageService {
|
|
|
1125
1125
|
await Promise.all(checkFileRequests);
|
|
1126
1126
|
// 文件路径不存在
|
|
1127
1127
|
if (notExistsFiles.length) {
|
|
1128
|
-
|
|
1128
|
+
(0, error_1.throwNotFound)(`以下文件不存在:${notExistsFiles.join(', ')}`);
|
|
1129
1129
|
}
|
|
1130
1130
|
const data = files.map(item => ({
|
|
1131
1131
|
fileid: this.cloudPathToFileId(item.cloudPath),
|
|
@@ -1153,11 +1153,11 @@ class StorageService {
|
|
|
1153
1153
|
*/
|
|
1154
1154
|
async deleteFile(cloudPathList) {
|
|
1155
1155
|
if (!cloudPathList || !Array.isArray(cloudPathList)) {
|
|
1156
|
-
|
|
1156
|
+
(0, error_1.throwInvalidParam)('fileList必须是非空的数组');
|
|
1157
1157
|
}
|
|
1158
1158
|
const hasInvalidFileId = cloudPathList.some(file => !file || typeof file !== 'string');
|
|
1159
1159
|
if (hasInvalidFileId) {
|
|
1160
|
-
|
|
1160
|
+
(0, error_1.throwInvalidParam)('fileList的元素必须是非空的字符串');
|
|
1161
1161
|
}
|
|
1162
1162
|
const { bucket, env } = this.getStorageConfig();
|
|
1163
1163
|
const fileIdList = cloudPathList.map(filePath => this.cloudPathToFileId(filePath));
|
|
@@ -1170,10 +1170,16 @@ class StorageService {
|
|
|
1170
1170
|
},
|
|
1171
1171
|
method: 'POST'
|
|
1172
1172
|
});
|
|
1173
|
+
(0, utils_1.assertDataPlaneOk)(res, 'storage.batchDeleteFile');
|
|
1173
1174
|
const failed = collectBatchDeleteFailures(res.data.delete_list, fileIdList, cloudPathList);
|
|
1174
1175
|
if (failed.length) {
|
|
1175
1176
|
const failedList = failed.map(item => `${item.fileId} : ${item.code}`);
|
|
1176
1177
|
throw new error_1.CloudBaseError(`部分删除文件失败:${JSON.stringify(failedList)}`, {
|
|
1178
|
+
// 稳定码:消费方据此判断「逐项结果可用」,而不是拿「某字段存不存在」猜 SDK 版本。
|
|
1179
|
+
// 与 cloudbase-cli/src/commands/storage/batch-delete.ts 的同名常量**一字不差**(跨仓契约)。
|
|
1180
|
+
code: 'STORAGE_BATCH_DELETE_PARTIAL_FAILED',
|
|
1181
|
+
source: 'manager-node',
|
|
1182
|
+
// 逐项结果放在 meta.failed(v5.8.8 起的既有契约,消费方 batch-delete.ts 按此读取)
|
|
1177
1183
|
meta: { failed, total: cloudPathList.length }
|
|
1178
1184
|
});
|
|
1179
1185
|
}
|
|
@@ -1188,11 +1194,11 @@ class StorageService {
|
|
|
1188
1194
|
*/
|
|
1189
1195
|
async deleteFileCustom(cloudPathList, bucket, region) {
|
|
1190
1196
|
if (!cloudPathList || !Array.isArray(cloudPathList)) {
|
|
1191
|
-
|
|
1197
|
+
(0, error_1.throwInvalidParam)('fileList必须是非空的数组');
|
|
1192
1198
|
}
|
|
1193
1199
|
const hasInvalidFileId = cloudPathList.some(file => !file || typeof file !== 'string');
|
|
1194
1200
|
if (hasInvalidFileId) {
|
|
1195
|
-
|
|
1201
|
+
(0, error_1.throwInvalidParam)('fileList的元素必须是非空的字符串');
|
|
1196
1202
|
}
|
|
1197
1203
|
const cos = this.getCos();
|
|
1198
1204
|
const deleteObject = util_1.default.promisify(cos.deleteObject).bind(cos);
|
|
@@ -1218,7 +1224,7 @@ class StorageService {
|
|
|
1218
1224
|
Key: (0, storage_config_1.toPhysicalKey)(basePath, cloudPath)
|
|
1219
1225
|
});
|
|
1220
1226
|
if (!headers) {
|
|
1221
|
-
|
|
1227
|
+
(0, error_1.throwApiError)(`[${cloudPath}] 获取文件信息失败`);
|
|
1222
1228
|
}
|
|
1223
1229
|
// 文件大小 KB
|
|
1224
1230
|
const size = Number(Number(headers['content-length']) / 1024).toFixed(2);
|
|
@@ -1339,10 +1345,10 @@ class StorageService {
|
|
|
1339
1345
|
async setStorageAcl(acl, rule) {
|
|
1340
1346
|
const validAcl = ['READONLY', 'PRIVATE', 'ADMINWRITE', 'ADMINONLY', 'CUSTOM'];
|
|
1341
1347
|
if (!validAcl.includes(acl)) {
|
|
1342
|
-
|
|
1348
|
+
(0, error_1.throwInvalidParam)(`非法的权限类型: "${acl}", 有效值: ${validAcl.join(', ')}`);
|
|
1343
1349
|
}
|
|
1344
1350
|
if (acl === 'CUSTOM' && !rule) {
|
|
1345
|
-
|
|
1351
|
+
(0, error_1.throwInvalidParam)('使用 CUSTOM 权限类型时,必须提供 rule 参数');
|
|
1346
1352
|
}
|
|
1347
1353
|
const { bucket, env } = this.getStorageConfig();
|
|
1348
1354
|
const params = {
|
|
@@ -1487,7 +1493,7 @@ class StorageService {
|
|
|
1487
1493
|
});
|
|
1488
1494
|
}
|
|
1489
1495
|
catch (e) {
|
|
1490
|
-
|
|
1496
|
+
(0, error_1.throwApiError)(e.message);
|
|
1491
1497
|
}
|
|
1492
1498
|
}
|
|
1493
1499
|
/**
|
|
@@ -1503,11 +1509,7 @@ class StorageService {
|
|
|
1503
1509
|
},
|
|
1504
1510
|
method: 'POST'
|
|
1505
1511
|
});
|
|
1506
|
-
|
|
1507
|
-
throw new error_1.CloudBaseError(`${res.code}: ${res.message || ''}`, {
|
|
1508
|
-
requestId: res.requestId
|
|
1509
|
-
});
|
|
1510
|
-
}
|
|
1512
|
+
(0, utils_1.assertDataPlaneOk)(res, 'storage.getUploadMetadata');
|
|
1511
1513
|
return res.data;
|
|
1512
1514
|
}
|
|
1513
1515
|
/**
|
|
@@ -1662,7 +1664,7 @@ class StorageService {
|
|
|
1662
1664
|
CopySource: copySource
|
|
1663
1665
|
});
|
|
1664
1666
|
if (res.statusCode !== 200) {
|
|
1665
|
-
|
|
1667
|
+
(0, error_1.throwApiError)(`复制文件失败:${JSON.stringify(res)}`);
|
|
1666
1668
|
}
|
|
1667
1669
|
// 4. 返回复制结果
|
|
1668
1670
|
return {
|
|
@@ -1952,19 +1954,19 @@ class StorageService {
|
|
|
1952
1954
|
async uploadObject(options) {
|
|
1953
1955
|
const { bucketId, objectName, localPath, body, contentType, contentLength, cacheControl, metadata, xRobotsTag, upsert = false, usePut = false, accessToken, envId: envIdOverride } = options;
|
|
1954
1956
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
1955
|
-
|
|
1957
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
1956
1958
|
}
|
|
1957
1959
|
if (!objectName || typeof objectName !== 'string') {
|
|
1958
|
-
|
|
1960
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
1959
1961
|
}
|
|
1960
1962
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
1961
|
-
|
|
1963
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
1962
1964
|
}
|
|
1963
1965
|
if (!localPath && !body) {
|
|
1964
|
-
|
|
1966
|
+
(0, error_1.throwInvalidParam)('localPath 与 body 至少提供一个');
|
|
1965
1967
|
}
|
|
1966
1968
|
if (localPath && body) {
|
|
1967
|
-
|
|
1969
|
+
(0, error_1.throwInvalidParam)('localPath 与 body 只能二选一');
|
|
1968
1970
|
}
|
|
1969
1971
|
// env / proxy
|
|
1970
1972
|
const authConfig = this.environment.getAuthConfig();
|
|
@@ -1986,7 +1988,7 @@ class StorageService {
|
|
|
1986
1988
|
(0, utils_1.checkFullAccess)(resolvedPath, true);
|
|
1987
1989
|
const stat = fs_1.default.statSync(resolvedPath);
|
|
1988
1990
|
if (!stat.isFile()) {
|
|
1989
|
-
|
|
1991
|
+
(0, error_1.throwInvalidParam)(`localPath 不是文件: ${localPath}`);
|
|
1990
1992
|
}
|
|
1991
1993
|
fileSize = stat.size;
|
|
1992
1994
|
}
|
|
@@ -1996,8 +1998,8 @@ class StorageService {
|
|
|
1996
1998
|
// 文档约束:> 100MB 拒绝
|
|
1997
1999
|
const LIMIT_100MB = 100 * 1024 * 1024;
|
|
1998
2000
|
if (fileSize !== undefined && fileSize > LIMIT_100MB) {
|
|
1999
|
-
throw new error_1.CloudBaseError('
|
|
2000
|
-
code: '
|
|
2001
|
+
throw new error_1.CloudBaseError('文件大小超过 100MB', {
|
|
2002
|
+
code: 'FILE_SIZE_LIMIT_EXCEEDED'
|
|
2001
2003
|
});
|
|
2002
2004
|
}
|
|
2003
2005
|
const finalContentType = this.resolveUploadContentType({
|
|
@@ -2044,9 +2046,9 @@ class StorageService {
|
|
|
2044
2046
|
}
|
|
2045
2047
|
catch (_a) {
|
|
2046
2048
|
if (!res.ok) {
|
|
2047
|
-
|
|
2049
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2048
2050
|
}
|
|
2049
|
-
|
|
2051
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2050
2052
|
}
|
|
2051
2053
|
}
|
|
2052
2054
|
if (!res.ok) {
|
|
@@ -2058,7 +2060,7 @@ class StorageService {
|
|
|
2058
2060
|
});
|
|
2059
2061
|
}
|
|
2060
2062
|
if (!payload || !payload.Id || !payload.Key) {
|
|
2061
|
-
|
|
2063
|
+
(0, error_1.throwApiError)('上传成功但响应格式异常:缺少 Id 或 Key');
|
|
2062
2064
|
}
|
|
2063
2065
|
return {
|
|
2064
2066
|
Id: payload.Id,
|
|
@@ -2072,13 +2074,13 @@ class StorageService {
|
|
|
2072
2074
|
async deleteObject(options) {
|
|
2073
2075
|
const { bucketId, objectName, accessToken, envId: envIdOverride } = options;
|
|
2074
2076
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2075
|
-
|
|
2077
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2076
2078
|
}
|
|
2077
2079
|
if (!objectName || typeof objectName !== 'string') {
|
|
2078
|
-
|
|
2080
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2079
2081
|
}
|
|
2080
2082
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2081
|
-
|
|
2083
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2082
2084
|
}
|
|
2083
2085
|
const env = this.environment.getEnvId();
|
|
2084
2086
|
const envId = envIdOverride || env;
|
|
@@ -2103,9 +2105,9 @@ class StorageService {
|
|
|
2103
2105
|
}
|
|
2104
2106
|
catch (_a) {
|
|
2105
2107
|
if (!res.ok) {
|
|
2106
|
-
|
|
2108
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2107
2109
|
}
|
|
2108
|
-
|
|
2110
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2109
2111
|
}
|
|
2110
2112
|
}
|
|
2111
2113
|
if (!res.ok) {
|
|
@@ -2128,20 +2130,20 @@ class StorageService {
|
|
|
2128
2130
|
async deleteObjects(options) {
|
|
2129
2131
|
const { bucketId, prefixes, accessToken, envId: envIdOverride } = options;
|
|
2130
2132
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2131
|
-
|
|
2133
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2132
2134
|
}
|
|
2133
2135
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2134
|
-
|
|
2136
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2135
2137
|
}
|
|
2136
2138
|
if (!Array.isArray(prefixes) || prefixes.length === 0) {
|
|
2137
|
-
|
|
2139
|
+
(0, error_1.throwInvalidParam)('prefixes 必须是非空数组');
|
|
2138
2140
|
}
|
|
2139
2141
|
if (prefixes.length > 20) {
|
|
2140
|
-
|
|
2142
|
+
(0, error_1.throwInvalidParam)('prefixes 最多 20 个');
|
|
2141
2143
|
}
|
|
2142
2144
|
for (const name of prefixes) {
|
|
2143
2145
|
if (!name || typeof name !== 'string') {
|
|
2144
|
-
|
|
2146
|
+
(0, error_1.throwInvalidParam)('prefixes 中的每一项必须是非空字符串');
|
|
2145
2147
|
}
|
|
2146
2148
|
}
|
|
2147
2149
|
const env = this.environment.getEnvId();
|
|
@@ -2165,9 +2167,9 @@ class StorageService {
|
|
|
2165
2167
|
}
|
|
2166
2168
|
catch (_a) {
|
|
2167
2169
|
if (!res.ok) {
|
|
2168
|
-
|
|
2170
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2169
2171
|
}
|
|
2170
|
-
|
|
2172
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2171
2173
|
}
|
|
2172
2174
|
}
|
|
2173
2175
|
if (!res.ok) {
|
|
@@ -2179,7 +2181,7 @@ class StorageService {
|
|
|
2179
2181
|
});
|
|
2180
2182
|
}
|
|
2181
2183
|
if (!Array.isArray(payload)) {
|
|
2182
|
-
|
|
2184
|
+
(0, error_1.throwApiError)('批量删除响应格式异常:期望数组');
|
|
2183
2185
|
}
|
|
2184
2186
|
return payload;
|
|
2185
2187
|
}
|
|
@@ -2191,29 +2193,29 @@ class StorageService {
|
|
|
2191
2193
|
async copyObject(options) {
|
|
2192
2194
|
const { bucketId, sourceKey, destinationKey, destinationBucket, metadata, userMetadata, copyMetadata, upsert, accessToken, envId: envIdOverride } = options;
|
|
2193
2195
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2194
|
-
|
|
2196
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2195
2197
|
}
|
|
2196
2198
|
if (!sourceKey || typeof sourceKey !== 'string') {
|
|
2197
|
-
|
|
2199
|
+
(0, error_1.throwInvalidParam)('sourceKey 必须是非空字符串');
|
|
2198
2200
|
}
|
|
2199
2201
|
if (!destinationKey || typeof destinationKey !== 'string') {
|
|
2200
|
-
|
|
2202
|
+
(0, error_1.throwInvalidParam)('destinationKey 必须是非空字符串');
|
|
2201
2203
|
}
|
|
2202
2204
|
if (destinationBucket !== undefined &&
|
|
2203
2205
|
(typeof destinationBucket !== 'string' || !destinationBucket)) {
|
|
2204
|
-
|
|
2206
|
+
(0, error_1.throwInvalidParam)('destinationBucket 必须是非空字符串');
|
|
2205
2207
|
}
|
|
2206
2208
|
if (metadata !== undefined && (typeof metadata !== 'object' || metadata === null || Array.isArray(metadata))) {
|
|
2207
|
-
|
|
2209
|
+
(0, error_1.throwInvalidParam)('metadata 必须是对象');
|
|
2208
2210
|
}
|
|
2209
2211
|
if (userMetadata !== undefined && (typeof userMetadata !== 'object' || userMetadata === null || Array.isArray(userMetadata))) {
|
|
2210
|
-
|
|
2212
|
+
(0, error_1.throwInvalidParam)('userMetadata 必须是对象');
|
|
2211
2213
|
}
|
|
2212
2214
|
if (copyMetadata !== undefined && typeof copyMetadata !== 'boolean') {
|
|
2213
|
-
|
|
2215
|
+
(0, error_1.throwInvalidParam)('copyMetadata 必须是布尔值');
|
|
2214
2216
|
}
|
|
2215
2217
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2216
|
-
|
|
2218
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2217
2219
|
}
|
|
2218
2220
|
const env = this.environment.getEnvId();
|
|
2219
2221
|
const envId = envIdOverride || env;
|
|
@@ -2254,9 +2256,9 @@ class StorageService {
|
|
|
2254
2256
|
}
|
|
2255
2257
|
catch (_a) {
|
|
2256
2258
|
if (!res.ok) {
|
|
2257
|
-
|
|
2259
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2258
2260
|
}
|
|
2259
|
-
|
|
2261
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2260
2262
|
}
|
|
2261
2263
|
}
|
|
2262
2264
|
if (!res.ok) {
|
|
@@ -2268,7 +2270,7 @@ class StorageService {
|
|
|
2268
2270
|
});
|
|
2269
2271
|
}
|
|
2270
2272
|
if (!payload || !payload.Id || !payload.Key) {
|
|
2271
|
-
|
|
2273
|
+
(0, error_1.throwApiError)('复制成功但响应格式异常:缺少 Id 或 Key');
|
|
2272
2274
|
}
|
|
2273
2275
|
return payload;
|
|
2274
2276
|
}
|
|
@@ -2279,20 +2281,20 @@ class StorageService {
|
|
|
2279
2281
|
async moveObject(options) {
|
|
2280
2282
|
const { bucketId, sourceKey, destinationKey, destinationBucket, accessToken, envId: envIdOverride } = options;
|
|
2281
2283
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2282
|
-
|
|
2284
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2283
2285
|
}
|
|
2284
2286
|
if (!sourceKey || typeof sourceKey !== 'string') {
|
|
2285
|
-
|
|
2287
|
+
(0, error_1.throwInvalidParam)('sourceKey 必须是非空字符串');
|
|
2286
2288
|
}
|
|
2287
2289
|
if (!destinationKey || typeof destinationKey !== 'string') {
|
|
2288
|
-
|
|
2290
|
+
(0, error_1.throwInvalidParam)('destinationKey 必须是非空字符串');
|
|
2289
2291
|
}
|
|
2290
2292
|
if (destinationBucket !== undefined &&
|
|
2291
2293
|
(typeof destinationBucket !== 'string' || !destinationBucket)) {
|
|
2292
|
-
|
|
2294
|
+
(0, error_1.throwInvalidParam)('destinationBucket 必须是非空字符串');
|
|
2293
2295
|
}
|
|
2294
2296
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2295
|
-
|
|
2297
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2296
2298
|
}
|
|
2297
2299
|
const env = this.environment.getEnvId();
|
|
2298
2300
|
const envId = envIdOverride || env;
|
|
@@ -2321,9 +2323,9 @@ class StorageService {
|
|
|
2321
2323
|
}
|
|
2322
2324
|
catch (_a) {
|
|
2323
2325
|
if (!res.ok) {
|
|
2324
|
-
|
|
2326
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2325
2327
|
}
|
|
2326
|
-
|
|
2328
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2327
2329
|
}
|
|
2328
2330
|
}
|
|
2329
2331
|
if (!res.ok) {
|
|
@@ -2335,7 +2337,7 @@ class StorageService {
|
|
|
2335
2337
|
});
|
|
2336
2338
|
}
|
|
2337
2339
|
if (!payload || !payload.Id || !payload.Key) {
|
|
2338
|
-
|
|
2340
|
+
(0, error_1.throwApiError)('移动成功但响应格式异常:缺少 Id 或 Key');
|
|
2339
2341
|
}
|
|
2340
2342
|
return {
|
|
2341
2343
|
message: payload.message || 'Successfully moved',
|
|
@@ -2351,41 +2353,41 @@ class StorageService {
|
|
|
2351
2353
|
async listObjects(options) {
|
|
2352
2354
|
const { bucketId, prefix, limit, cursor, withDelimiter, sortBy, accessToken, envId: envIdOverride } = options;
|
|
2353
2355
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2354
|
-
|
|
2356
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2355
2357
|
}
|
|
2356
2358
|
if (prefix !== undefined && typeof prefix !== 'string') {
|
|
2357
|
-
|
|
2359
|
+
(0, error_1.throwInvalidParam)('prefix 必须是字符串');
|
|
2358
2360
|
}
|
|
2359
2361
|
if (limit !== undefined) {
|
|
2360
2362
|
if (!Number.isInteger(limit)) {
|
|
2361
|
-
|
|
2363
|
+
(0, error_1.throwInvalidParam)('limit 必须是整数');
|
|
2362
2364
|
}
|
|
2363
2365
|
if (limit < 1 || limit > 1000) {
|
|
2364
|
-
|
|
2366
|
+
(0, error_1.throwInvalidParam)('limit 必须在 1 ~ 1000 之间');
|
|
2365
2367
|
}
|
|
2366
2368
|
}
|
|
2367
2369
|
if (cursor !== undefined && typeof cursor !== 'string') {
|
|
2368
|
-
|
|
2370
|
+
(0, error_1.throwInvalidParam)('cursor 必须是字符串');
|
|
2369
2371
|
}
|
|
2370
2372
|
if (withDelimiter !== undefined && typeof withDelimiter !== 'boolean') {
|
|
2371
|
-
|
|
2373
|
+
(0, error_1.throwInvalidParam)('withDelimiter 必须是布尔值');
|
|
2372
2374
|
}
|
|
2373
2375
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2374
|
-
|
|
2376
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2375
2377
|
}
|
|
2376
2378
|
if (sortBy !== undefined) {
|
|
2377
2379
|
if (typeof sortBy !== 'object' || sortBy === null || Array.isArray(sortBy)) {
|
|
2378
|
-
|
|
2380
|
+
(0, error_1.throwInvalidParam)('sortBy 必须是对象');
|
|
2379
2381
|
}
|
|
2380
2382
|
const allowedColumns = ['name', 'created_at', 'updated_at'];
|
|
2381
2383
|
const allowedOrders = ['asc', 'desc'];
|
|
2382
2384
|
if (sortBy.column !== undefined && !allowedColumns.includes(sortBy.column)) {
|
|
2383
|
-
|
|
2385
|
+
(0, error_1.throwInvalidParam)(`sortBy.column 非法,允许值:${allowedColumns.join('、')}`);
|
|
2384
2386
|
}
|
|
2385
2387
|
if (sortBy.order !== undefined) {
|
|
2386
2388
|
const normalizedOrder = String(sortBy.order).toLowerCase();
|
|
2387
2389
|
if (!allowedOrders.includes(normalizedOrder)) {
|
|
2388
|
-
|
|
2390
|
+
(0, error_1.throwInvalidParam)(`sortBy.order 非法,允许值:${allowedOrders.join('、')}`);
|
|
2389
2391
|
}
|
|
2390
2392
|
sortBy.order = normalizedOrder;
|
|
2391
2393
|
}
|
|
@@ -2422,9 +2424,9 @@ class StorageService {
|
|
|
2422
2424
|
}
|
|
2423
2425
|
catch (_a) {
|
|
2424
2426
|
if (!res.ok) {
|
|
2425
|
-
|
|
2427
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2426
2428
|
}
|
|
2427
|
-
|
|
2429
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2428
2430
|
}
|
|
2429
2431
|
}
|
|
2430
2432
|
if (!res.ok) {
|
|
@@ -2436,7 +2438,7 @@ class StorageService {
|
|
|
2436
2438
|
});
|
|
2437
2439
|
}
|
|
2438
2440
|
if (!payload || !Array.isArray(payload.objects) || !Array.isArray(payload.folders)) {
|
|
2439
|
-
|
|
2441
|
+
(0, error_1.throwApiError)('列出对象成功但响应格式异常:缺少 folders 或 objects 数组');
|
|
2440
2442
|
}
|
|
2441
2443
|
const result = Object.assign({ folders: payload.folders, objects: payload.objects, hasNext: Boolean(payload.hasNext), nextCursor: payload.nextCursor, nextCursorKey: payload.nextCursorKey }, payload);
|
|
2442
2444
|
return result;
|
|
@@ -2448,19 +2450,19 @@ class StorageService {
|
|
|
2448
2450
|
async signObject(options) {
|
|
2449
2451
|
const { bucketId, objectName, expiresIn, accessToken, envId: envIdOverride } = options;
|
|
2450
2452
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2451
|
-
|
|
2453
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2452
2454
|
}
|
|
2453
2455
|
if (!objectName || typeof objectName !== 'string') {
|
|
2454
|
-
|
|
2456
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2455
2457
|
}
|
|
2456
2458
|
if (!Number.isInteger(expiresIn)) {
|
|
2457
|
-
|
|
2459
|
+
(0, error_1.throwInvalidParam)('expiresIn 必须是整数');
|
|
2458
2460
|
}
|
|
2459
2461
|
if (expiresIn < 1) {
|
|
2460
|
-
|
|
2462
|
+
(0, error_1.throwInvalidParam)('expiresIn 必须 >= 1');
|
|
2461
2463
|
}
|
|
2462
2464
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2463
|
-
|
|
2465
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2464
2466
|
}
|
|
2465
2467
|
const env = this.environment.getEnvId();
|
|
2466
2468
|
const envId = envIdOverride || env;
|
|
@@ -2487,9 +2489,9 @@ class StorageService {
|
|
|
2487
2489
|
}
|
|
2488
2490
|
catch (_a) {
|
|
2489
2491
|
if (!res.ok) {
|
|
2490
|
-
|
|
2492
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2491
2493
|
}
|
|
2492
|
-
|
|
2494
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2493
2495
|
}
|
|
2494
2496
|
}
|
|
2495
2497
|
if (!res.ok) {
|
|
@@ -2501,7 +2503,7 @@ class StorageService {
|
|
|
2501
2503
|
});
|
|
2502
2504
|
}
|
|
2503
2505
|
if (!payload || typeof payload.signedURL !== 'string' || !payload.signedURL) {
|
|
2504
|
-
|
|
2506
|
+
(0, error_1.throwApiError)('生成签名 URL 成功但响应格式异常:缺少 signedURL');
|
|
2505
2507
|
}
|
|
2506
2508
|
return {
|
|
2507
2509
|
signedURL: payload.signedURL
|
|
@@ -2514,27 +2516,27 @@ class StorageService {
|
|
|
2514
2516
|
async signObjects(options) {
|
|
2515
2517
|
const { bucketId, expiresIn, paths, accessToken, envId: envIdOverride } = options;
|
|
2516
2518
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2517
|
-
|
|
2519
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2518
2520
|
}
|
|
2519
2521
|
if (!Number.isInteger(expiresIn)) {
|
|
2520
|
-
|
|
2522
|
+
(0, error_1.throwInvalidParam)('expiresIn 必须是整数');
|
|
2521
2523
|
}
|
|
2522
2524
|
if (expiresIn < 1) {
|
|
2523
|
-
|
|
2525
|
+
(0, error_1.throwInvalidParam)('expiresIn 必须 >= 1');
|
|
2524
2526
|
}
|
|
2525
2527
|
if (!Array.isArray(paths) || paths.length === 0) {
|
|
2526
|
-
|
|
2528
|
+
(0, error_1.throwInvalidParam)('paths 必须是非空数组');
|
|
2527
2529
|
}
|
|
2528
2530
|
if (paths.length > 500) {
|
|
2529
|
-
|
|
2531
|
+
(0, error_1.throwInvalidParam)('paths 最多 500 个');
|
|
2530
2532
|
}
|
|
2531
2533
|
for (const pathItem of paths) {
|
|
2532
2534
|
if (!pathItem || typeof pathItem !== 'string') {
|
|
2533
|
-
|
|
2535
|
+
(0, error_1.throwInvalidParam)('paths 中每一项必须是非空字符串');
|
|
2534
2536
|
}
|
|
2535
2537
|
}
|
|
2536
2538
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2537
|
-
|
|
2539
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2538
2540
|
}
|
|
2539
2541
|
const env = this.environment.getEnvId();
|
|
2540
2542
|
const envId = envIdOverride || env;
|
|
@@ -2560,9 +2562,9 @@ class StorageService {
|
|
|
2560
2562
|
}
|
|
2561
2563
|
catch (_a) {
|
|
2562
2564
|
if (!res.ok) {
|
|
2563
|
-
|
|
2565
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2564
2566
|
}
|
|
2565
|
-
|
|
2567
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2566
2568
|
}
|
|
2567
2569
|
}
|
|
2568
2570
|
if (!res.ok) {
|
|
@@ -2574,7 +2576,7 @@ class StorageService {
|
|
|
2574
2576
|
});
|
|
2575
2577
|
}
|
|
2576
2578
|
if (!Array.isArray(payload)) {
|
|
2577
|
-
|
|
2579
|
+
(0, error_1.throwApiError)('批量生成签名 URL 成功但响应格式异常:期望数组');
|
|
2578
2580
|
}
|
|
2579
2581
|
return payload;
|
|
2580
2582
|
}
|
|
@@ -2585,16 +2587,16 @@ class StorageService {
|
|
|
2585
2587
|
async signUploadObject(options) {
|
|
2586
2588
|
const { bucketId, objectName, upsert = false, accessToken, envId: envIdOverride } = options;
|
|
2587
2589
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2588
|
-
|
|
2590
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2589
2591
|
}
|
|
2590
2592
|
if (!objectName || typeof objectName !== 'string') {
|
|
2591
|
-
|
|
2593
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2592
2594
|
}
|
|
2593
2595
|
if (typeof upsert !== 'boolean') {
|
|
2594
|
-
|
|
2596
|
+
(0, error_1.throwInvalidParam)('upsert 必须是布尔值');
|
|
2595
2597
|
}
|
|
2596
2598
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2597
|
-
|
|
2599
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2598
2600
|
}
|
|
2599
2601
|
const env = this.environment.getEnvId();
|
|
2600
2602
|
const envId = envIdOverride || env;
|
|
@@ -2623,9 +2625,9 @@ class StorageService {
|
|
|
2623
2625
|
}
|
|
2624
2626
|
catch (_a) {
|
|
2625
2627
|
if (!res.ok) {
|
|
2626
|
-
|
|
2628
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2627
2629
|
}
|
|
2628
|
-
|
|
2630
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2629
2631
|
}
|
|
2630
2632
|
}
|
|
2631
2633
|
if (!res.ok) {
|
|
@@ -2637,7 +2639,7 @@ class StorageService {
|
|
|
2637
2639
|
});
|
|
2638
2640
|
}
|
|
2639
2641
|
if (!payload || typeof payload.url !== 'string' || typeof payload.token !== 'string') {
|
|
2640
|
-
|
|
2642
|
+
(0, error_1.throwApiError)('生成签名上传 URL 成功但响应格式异常:缺少 url 或 token');
|
|
2641
2643
|
}
|
|
2642
2644
|
return {
|
|
2643
2645
|
url: payload.url,
|
|
@@ -2651,19 +2653,19 @@ class StorageService {
|
|
|
2651
2653
|
async uploadObjectBySign(options) {
|
|
2652
2654
|
const { bucketId, objectName, token, localPath, body, contentType, contentLength, envId: envIdOverride } = options;
|
|
2653
2655
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2654
|
-
|
|
2656
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2655
2657
|
}
|
|
2656
2658
|
if (!objectName || typeof objectName !== 'string') {
|
|
2657
|
-
|
|
2659
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2658
2660
|
}
|
|
2659
2661
|
if (!token || typeof token !== 'string') {
|
|
2660
|
-
|
|
2662
|
+
(0, error_1.throwInvalidParam)('token 必须是非空字符串');
|
|
2661
2663
|
}
|
|
2662
2664
|
if (!localPath && !body) {
|
|
2663
|
-
|
|
2665
|
+
(0, error_1.throwInvalidParam)('localPath 与 body 至少提供一个');
|
|
2664
2666
|
}
|
|
2665
2667
|
if (localPath && body) {
|
|
2666
|
-
|
|
2668
|
+
(0, error_1.throwInvalidParam)('localPath 与 body 只能二选一');
|
|
2667
2669
|
}
|
|
2668
2670
|
const env = this.environment.getEnvId();
|
|
2669
2671
|
const envId = envIdOverride || env;
|
|
@@ -2680,7 +2682,7 @@ class StorageService {
|
|
|
2680
2682
|
(0, utils_1.checkFullAccess)(resolvedPath, true);
|
|
2681
2683
|
const stat = fs_1.default.statSync(resolvedPath);
|
|
2682
2684
|
if (!stat.isFile()) {
|
|
2683
|
-
|
|
2685
|
+
(0, error_1.throwInvalidParam)(`localPath 不是文件: ${localPath}`);
|
|
2684
2686
|
}
|
|
2685
2687
|
fileSize = stat.size;
|
|
2686
2688
|
}
|
|
@@ -2715,9 +2717,9 @@ class StorageService {
|
|
|
2715
2717
|
}
|
|
2716
2718
|
catch (_a) {
|
|
2717
2719
|
if (!res.ok) {
|
|
2718
|
-
|
|
2720
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2719
2721
|
}
|
|
2720
|
-
|
|
2722
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
2721
2723
|
}
|
|
2722
2724
|
}
|
|
2723
2725
|
if (!res.ok) {
|
|
@@ -2729,7 +2731,7 @@ class StorageService {
|
|
|
2729
2731
|
});
|
|
2730
2732
|
}
|
|
2731
2733
|
if (!payload || typeof payload.Key !== 'string' || !payload.Key) {
|
|
2732
|
-
|
|
2734
|
+
(0, error_1.throwApiError)('签名上传成功但响应格式异常:缺少 Key');
|
|
2733
2735
|
}
|
|
2734
2736
|
return {
|
|
2735
2737
|
Key: payload.Key
|
|
@@ -2742,18 +2744,18 @@ class StorageService {
|
|
|
2742
2744
|
async downloadAuthenticatedObject(options) {
|
|
2743
2745
|
const { bucketId, objectName, method = 'GET', download, ifNoneMatch, ifModifiedSince, range, accessToken, envId: envIdOverride } = options;
|
|
2744
2746
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2745
|
-
|
|
2747
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2746
2748
|
}
|
|
2747
2749
|
if (!objectName || typeof objectName !== 'string') {
|
|
2748
|
-
|
|
2750
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2749
2751
|
}
|
|
2750
2752
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2751
|
-
|
|
2753
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
2752
2754
|
}
|
|
2753
2755
|
const normalizedMethod = String(method).toUpperCase();
|
|
2754
2756
|
const allowedMethods = ['GET', 'HEAD'];
|
|
2755
2757
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2756
|
-
|
|
2758
|
+
(0, error_1.throwInvalidParam)('method 仅支持 GET 或 HEAD');
|
|
2757
2759
|
}
|
|
2758
2760
|
const env = this.environment.getEnvId();
|
|
2759
2761
|
const envId = envIdOverride || env;
|
|
@@ -2765,7 +2767,7 @@ class StorageService {
|
|
|
2765
2767
|
let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/authenticated/${encodedBucketId}/${encodedObjectName}`;
|
|
2766
2768
|
if (download !== undefined) {
|
|
2767
2769
|
if (typeof download !== 'string') {
|
|
2768
|
-
|
|
2770
|
+
(0, error_1.throwInvalidParam)('download 必须是字符串');
|
|
2769
2771
|
}
|
|
2770
2772
|
url += `?download=${encodeURIComponent(download)}`;
|
|
2771
2773
|
}
|
|
@@ -2774,19 +2776,19 @@ class StorageService {
|
|
|
2774
2776
|
};
|
|
2775
2777
|
if (ifNoneMatch !== undefined) {
|
|
2776
2778
|
if (typeof ifNoneMatch !== 'string') {
|
|
2777
|
-
|
|
2779
|
+
(0, error_1.throwInvalidParam)('ifNoneMatch 必须是字符串');
|
|
2778
2780
|
}
|
|
2779
2781
|
headers['If-None-Match'] = ifNoneMatch;
|
|
2780
2782
|
}
|
|
2781
2783
|
if (ifModifiedSince !== undefined) {
|
|
2782
2784
|
if (typeof ifModifiedSince !== 'string') {
|
|
2783
|
-
|
|
2785
|
+
(0, error_1.throwInvalidParam)('ifModifiedSince 必须是字符串');
|
|
2784
2786
|
}
|
|
2785
2787
|
headers['If-Modified-Since'] = ifModifiedSince;
|
|
2786
2788
|
}
|
|
2787
2789
|
if (range !== undefined) {
|
|
2788
2790
|
if (typeof range !== 'string') {
|
|
2789
|
-
|
|
2791
|
+
(0, error_1.throwInvalidParam)('range 必须是字符串');
|
|
2790
2792
|
}
|
|
2791
2793
|
headers.Range = range;
|
|
2792
2794
|
}
|
|
@@ -2803,7 +2805,7 @@ class StorageService {
|
|
|
2803
2805
|
payload = JSON.parse(text);
|
|
2804
2806
|
}
|
|
2805
2807
|
catch (_a) {
|
|
2806
|
-
|
|
2808
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2807
2809
|
}
|
|
2808
2810
|
}
|
|
2809
2811
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -2830,18 +2832,18 @@ class StorageService {
|
|
|
2830
2832
|
async downloadObject(options) {
|
|
2831
2833
|
const { bucketId, objectName, method = 'GET', download, ifNoneMatch, ifModifiedSince, range, accessToken, envId: envIdOverride } = options;
|
|
2832
2834
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2833
|
-
|
|
2835
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2834
2836
|
}
|
|
2835
2837
|
if (!objectName || typeof objectName !== 'string') {
|
|
2836
|
-
|
|
2838
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2837
2839
|
}
|
|
2838
2840
|
if (accessToken !== undefined && typeof accessToken !== 'string') {
|
|
2839
|
-
|
|
2841
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是字符串');
|
|
2840
2842
|
}
|
|
2841
2843
|
const normalizedMethod = String(method).toUpperCase();
|
|
2842
2844
|
const allowedMethods = ['GET', 'HEAD'];
|
|
2843
2845
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2844
|
-
|
|
2846
|
+
(0, error_1.throwInvalidParam)('method 仅支持 GET 或 HEAD');
|
|
2845
2847
|
}
|
|
2846
2848
|
const env = this.environment.getEnvId();
|
|
2847
2849
|
const envId = envIdOverride || env;
|
|
@@ -2853,7 +2855,7 @@ class StorageService {
|
|
|
2853
2855
|
let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}/${encodedObjectName}`;
|
|
2854
2856
|
if (download !== undefined) {
|
|
2855
2857
|
if (typeof download !== 'string') {
|
|
2856
|
-
|
|
2858
|
+
(0, error_1.throwInvalidParam)('download 必须是字符串');
|
|
2857
2859
|
}
|
|
2858
2860
|
url += `?download=${encodeURIComponent(download)}`;
|
|
2859
2861
|
}
|
|
@@ -2863,19 +2865,19 @@ class StorageService {
|
|
|
2863
2865
|
}
|
|
2864
2866
|
if (ifNoneMatch !== undefined) {
|
|
2865
2867
|
if (typeof ifNoneMatch !== 'string') {
|
|
2866
|
-
|
|
2868
|
+
(0, error_1.throwInvalidParam)('ifNoneMatch 必须是字符串');
|
|
2867
2869
|
}
|
|
2868
2870
|
headers['If-None-Match'] = ifNoneMatch;
|
|
2869
2871
|
}
|
|
2870
2872
|
if (ifModifiedSince !== undefined) {
|
|
2871
2873
|
if (typeof ifModifiedSince !== 'string') {
|
|
2872
|
-
|
|
2874
|
+
(0, error_1.throwInvalidParam)('ifModifiedSince 必须是字符串');
|
|
2873
2875
|
}
|
|
2874
2876
|
headers['If-Modified-Since'] = ifModifiedSince;
|
|
2875
2877
|
}
|
|
2876
2878
|
if (range !== undefined) {
|
|
2877
2879
|
if (typeof range !== 'string') {
|
|
2878
|
-
|
|
2880
|
+
(0, error_1.throwInvalidParam)('range 必须是字符串');
|
|
2879
2881
|
}
|
|
2880
2882
|
headers.Range = range;
|
|
2881
2883
|
}
|
|
@@ -2892,7 +2894,7 @@ class StorageService {
|
|
|
2892
2894
|
payload = JSON.parse(text);
|
|
2893
2895
|
}
|
|
2894
2896
|
catch (_a) {
|
|
2895
|
-
|
|
2897
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2896
2898
|
}
|
|
2897
2899
|
}
|
|
2898
2900
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -2919,15 +2921,15 @@ class StorageService {
|
|
|
2919
2921
|
async downloadPublicObject(options) {
|
|
2920
2922
|
const { bucketId, objectName, method = 'GET', download, envId: envIdOverride } = options;
|
|
2921
2923
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2922
|
-
|
|
2924
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2923
2925
|
}
|
|
2924
2926
|
if (!objectName || typeof objectName !== 'string') {
|
|
2925
|
-
|
|
2927
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2926
2928
|
}
|
|
2927
2929
|
const normalizedMethod = String(method).toUpperCase();
|
|
2928
2930
|
const allowedMethods = ['GET', 'HEAD'];
|
|
2929
2931
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2930
|
-
|
|
2932
|
+
(0, error_1.throwInvalidParam)('method 仅支持 GET 或 HEAD');
|
|
2931
2933
|
}
|
|
2932
2934
|
const env = this.environment.getEnvId();
|
|
2933
2935
|
const envId = envIdOverride || env;
|
|
@@ -2939,7 +2941,7 @@ class StorageService {
|
|
|
2939
2941
|
let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/public/${encodedBucketId}/${encodedObjectName}`;
|
|
2940
2942
|
if (download !== undefined) {
|
|
2941
2943
|
if (typeof download !== 'string') {
|
|
2942
|
-
|
|
2944
|
+
(0, error_1.throwInvalidParam)('download 必须是字符串');
|
|
2943
2945
|
}
|
|
2944
2946
|
url += `?download=${encodeURIComponent(download)}`;
|
|
2945
2947
|
}
|
|
@@ -2955,7 +2957,7 @@ class StorageService {
|
|
|
2955
2957
|
payload = JSON.parse(text);
|
|
2956
2958
|
}
|
|
2957
2959
|
catch (_a) {
|
|
2958
|
-
|
|
2960
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
2959
2961
|
}
|
|
2960
2962
|
}
|
|
2961
2963
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -2982,18 +2984,18 @@ class StorageService {
|
|
|
2982
2984
|
async downloadObjectBySign(options) {
|
|
2983
2985
|
const { bucketId, objectName, token, method = 'GET', download, envId: envIdOverride } = options;
|
|
2984
2986
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
2985
|
-
|
|
2987
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
2986
2988
|
}
|
|
2987
2989
|
if (!objectName || typeof objectName !== 'string') {
|
|
2988
|
-
|
|
2990
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
2989
2991
|
}
|
|
2990
2992
|
if (!token || typeof token !== 'string') {
|
|
2991
|
-
|
|
2993
|
+
(0, error_1.throwInvalidParam)('token 必须是非空字符串');
|
|
2992
2994
|
}
|
|
2993
2995
|
const normalizedMethod = String(method).toUpperCase();
|
|
2994
2996
|
const allowedMethods = ['GET', 'HEAD'];
|
|
2995
2997
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2996
|
-
|
|
2998
|
+
(0, error_1.throwInvalidParam)('method 仅支持 GET 或 HEAD');
|
|
2997
2999
|
}
|
|
2998
3000
|
const env = this.environment.getEnvId();
|
|
2999
3001
|
const envId = envIdOverride || env;
|
|
@@ -3005,7 +3007,7 @@ class StorageService {
|
|
|
3005
3007
|
let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/sign/${encodedBucketId}/${encodedObjectName}?token=${encodeURIComponent(token)}`;
|
|
3006
3008
|
if (download !== undefined) {
|
|
3007
3009
|
if (typeof download !== 'string') {
|
|
3008
|
-
|
|
3010
|
+
(0, error_1.throwInvalidParam)('download 必须是字符串');
|
|
3009
3011
|
}
|
|
3010
3012
|
url += `&download=${encodeURIComponent(download)}`;
|
|
3011
3013
|
}
|
|
@@ -3021,7 +3023,7 @@ class StorageService {
|
|
|
3021
3023
|
payload = JSON.parse(text);
|
|
3022
3024
|
}
|
|
3023
3025
|
catch (_a) {
|
|
3024
|
-
|
|
3026
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
3025
3027
|
}
|
|
3026
3028
|
}
|
|
3027
3029
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -3048,18 +3050,18 @@ class StorageService {
|
|
|
3048
3050
|
async getObjectInfoAuthenticated(options) {
|
|
3049
3051
|
const { bucketId, objectName, method = 'GET', accessToken, envId: envIdOverride } = options;
|
|
3050
3052
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
3051
|
-
|
|
3053
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
3052
3054
|
}
|
|
3053
3055
|
if (!objectName || typeof objectName !== 'string') {
|
|
3054
|
-
|
|
3056
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
3055
3057
|
}
|
|
3056
3058
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
3057
|
-
|
|
3059
|
+
(0, error_1.throwInvalidParam)('accessToken 必须是非空字符串');
|
|
3058
3060
|
}
|
|
3059
3061
|
const normalizedMethod = String(method).toUpperCase();
|
|
3060
3062
|
const allowedMethods = ['GET', 'HEAD'];
|
|
3061
3063
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
3062
|
-
|
|
3064
|
+
(0, error_1.throwInvalidParam)('method 仅支持 GET 或 HEAD');
|
|
3063
3065
|
}
|
|
3064
3066
|
const env = this.environment.getEnvId();
|
|
3065
3067
|
const envId = envIdOverride || env;
|
|
@@ -3084,7 +3086,7 @@ class StorageService {
|
|
|
3084
3086
|
payload = JSON.parse(text);
|
|
3085
3087
|
}
|
|
3086
3088
|
catch (_a) {
|
|
3087
|
-
|
|
3089
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
3088
3090
|
}
|
|
3089
3091
|
}
|
|
3090
3092
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -3112,11 +3114,11 @@ class StorageService {
|
|
|
3112
3114
|
payload = JSON.parse(text);
|
|
3113
3115
|
}
|
|
3114
3116
|
catch (_b) {
|
|
3115
|
-
|
|
3117
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
3116
3118
|
}
|
|
3117
3119
|
}
|
|
3118
3120
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
3119
|
-
|
|
3121
|
+
(0, error_1.throwApiError)('获取对象元信息成功但响应格式异常:期望 JSON 对象');
|
|
3120
3122
|
}
|
|
3121
3123
|
return {
|
|
3122
3124
|
status: res.status,
|
|
@@ -3131,10 +3133,10 @@ class StorageService {
|
|
|
3131
3133
|
async getObjectInfoPublic(options) {
|
|
3132
3134
|
const { bucketId, objectName, envId: envIdOverride } = options;
|
|
3133
3135
|
if (!bucketId || typeof bucketId !== 'string') {
|
|
3134
|
-
|
|
3136
|
+
(0, error_1.throwInvalidParam)('bucketId 必须是非空字符串');
|
|
3135
3137
|
}
|
|
3136
3138
|
if (!objectName || typeof objectName !== 'string') {
|
|
3137
|
-
|
|
3139
|
+
(0, error_1.throwInvalidParam)('objectName 必须是非空字符串');
|
|
3138
3140
|
}
|
|
3139
3141
|
const env = this.environment.getEnvId();
|
|
3140
3142
|
const envId = envIdOverride || env;
|
|
@@ -3156,7 +3158,7 @@ class StorageService {
|
|
|
3156
3158
|
payload = JSON.parse(text);
|
|
3157
3159
|
}
|
|
3158
3160
|
catch (_a) {
|
|
3159
|
-
|
|
3161
|
+
(0, error_1.throwApiError)(`HTTP_${res.status}: ${text}`);
|
|
3160
3162
|
}
|
|
3161
3163
|
}
|
|
3162
3164
|
const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
|
|
@@ -3177,11 +3179,11 @@ class StorageService {
|
|
|
3177
3179
|
payload = JSON.parse(text);
|
|
3178
3180
|
}
|
|
3179
3181
|
catch (_b) {
|
|
3180
|
-
|
|
3182
|
+
(0, error_1.throwApiError)(`响应不是合法 JSON: ${text}`);
|
|
3181
3183
|
}
|
|
3182
3184
|
}
|
|
3183
3185
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
3184
|
-
|
|
3186
|
+
(0, error_1.throwApiError)('获取公开对象元信息成功但响应格式异常:期望 JSON 对象');
|
|
3185
3187
|
}
|
|
3186
3188
|
return {
|
|
3187
3189
|
status: res.status,
|
|
@@ -3398,16 +3400,16 @@ class StorageService {
|
|
|
3398
3400
|
assertValidContentType(input) {
|
|
3399
3401
|
const ct = input.trim();
|
|
3400
3402
|
if (!ct)
|
|
3401
|
-
|
|
3403
|
+
(0, error_1.throwInvalidParam)('contentType 不能为空');
|
|
3402
3404
|
// 防 header 注入 / 控制字符
|
|
3403
3405
|
if (/[\u0000-\u001F\u007F]/.test(ct)) {
|
|
3404
|
-
|
|
3406
|
+
(0, error_1.throwInvalidParam)('contentType 非法:包含控制字符');
|
|
3405
3407
|
}
|
|
3406
3408
|
// 只校验主类型(; 后参数放宽)
|
|
3407
3409
|
const base = ct.split(';', 1)[0].trim();
|
|
3408
3410
|
const typeSubtypeRe = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
3409
3411
|
if (!typeSubtypeRe.test(base)) {
|
|
3410
|
-
|
|
3412
|
+
(0, error_1.throwInvalidParam)('contentType 非法:主类型格式不正确');
|
|
3411
3413
|
}
|
|
3412
3414
|
return ct;
|
|
3413
3415
|
}
|