@cloudbase/manager-node 4.2.2 → 4.2.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/package.json +4 -3
  3. package/lib/access/index.js +0 -141
  4. package/lib/access/types.js +0 -2
  5. package/lib/billing/index.js +0 -36
  6. package/lib/cam/index.js +0 -77
  7. package/lib/cloudBaseRun/index.js +0 -36
  8. package/lib/cloudBaseRun/types.js +0 -2
  9. package/lib/common/index.js +0 -39
  10. package/lib/constant.js +0 -51
  11. package/lib/context.js +0 -14
  12. package/lib/database/index.js +0 -244
  13. package/lib/debug.js +0 -34
  14. package/lib/env/index.js +0 -488
  15. package/lib/environment.js +0 -124
  16. package/lib/environmentManager.js +0 -44
  17. package/lib/error.js +0 -16
  18. package/lib/function/index.js +0 -1022
  19. package/lib/function/packer.js +0 -129
  20. package/lib/function/types.js +0 -2
  21. package/lib/hosting/index.js +0 -461
  22. package/lib/index.js +0 -83
  23. package/lib/interfaces/base.interface.js +0 -2
  24. package/lib/interfaces/billing.interface.js +0 -2
  25. package/lib/interfaces/cam.interface.js +0 -2
  26. package/lib/interfaces/flexdb.interface.js +0 -2
  27. package/lib/interfaces/function.interface.js +0 -2
  28. package/lib/interfaces/index.js +0 -19
  29. package/lib/interfaces/storage.interface.js +0 -2
  30. package/lib/interfaces/tcb.interface.js +0 -2
  31. package/lib/storage/index.js +0 -1035
  32. package/lib/third/index.js +0 -18
  33. package/lib/user/index.js +0 -136
  34. package/lib/user/types.js +0 -2
  35. package/lib/utils/auth.js +0 -97
  36. package/lib/utils/cloud-api-request.js +0 -205
  37. package/lib/utils/cloudbase-request.js +0 -69
  38. package/lib/utils/envLazy.js +0 -18
  39. package/lib/utils/fs.js +0 -64
  40. package/lib/utils/http-request.js +0 -44
  41. package/lib/utils/index.js +0 -103
  42. package/lib/utils/parallel.js +0 -69
  43. package/lib/utils/uuid.js +0 -18
  44. package/src/access/index.ts +0 -168
  45. package/src/access/types.ts +0 -55
  46. package/src/billing/index.ts +0 -43
  47. package/src/cam/index.ts +0 -106
  48. package/src/cloudBaseRun/index.ts +0 -40
  49. package/src/cloudBaseRun/types.ts +0 -24
  50. package/src/common/index.ts +0 -54
  51. package/src/constant.ts +0 -56
  52. package/src/context.ts +0 -18
  53. package/src/database/index.ts +0 -369
  54. package/src/debug.ts +0 -34
  55. package/src/env/index.ts +0 -614
  56. package/src/environment.ts +0 -156
  57. package/src/environmentManager.ts +0 -50
  58. package/src/error.ts +0 -27
  59. package/src/function/index.ts +0 -1378
  60. package/src/function/packer.ts +0 -164
  61. package/src/function/types.ts +0 -165
  62. package/src/hosting/index.ts +0 -698
  63. package/src/index.ts +0 -127
  64. package/src/interfaces/base.interface.ts +0 -8
  65. package/src/interfaces/billing.interface.ts +0 -21
  66. package/src/interfaces/cam.interface.ts +0 -28
  67. package/src/interfaces/flexdb.interface.ts +0 -104
  68. package/src/interfaces/function.interface.ts +0 -75
  69. package/src/interfaces/index.ts +0 -7
  70. package/src/interfaces/storage.interface.ts +0 -29
  71. package/src/interfaces/tcb.interface.ts +0 -642
  72. package/src/storage/index.ts +0 -1281
  73. package/src/third/index.ts +0 -24
  74. package/src/user/index.ts +0 -174
  75. package/src/user/types.ts +0 -21
  76. package/src/utils/auth.ts +0 -112
  77. package/src/utils/cloud-api-request.ts +0 -252
  78. package/src/utils/cloudbase-request.ts +0 -109
  79. package/src/utils/envLazy.ts +0 -15
  80. package/src/utils/fs.ts +0 -57
  81. package/src/utils/http-request.ts +0 -37
  82. package/src/utils/index.ts +0 -103
  83. package/src/utils/parallel.ts +0 -82
  84. package/src/utils/uuid.ts +0 -14
  85. package/types/access/index.d.ts +0 -38
  86. package/types/access/types.d.ts +0 -42
  87. package/types/billing/index.d.ts +0 -21
  88. package/types/cam/index.d.ts +0 -63
  89. package/types/cloudBaseRun/index.d.ts +0 -12
  90. package/types/cloudBaseRun/types.d.ts +0 -21
  91. package/types/common/index.d.ts +0 -18
  92. package/types/constant.d.ts +0 -37
  93. package/types/context.d.ts +0 -17
  94. package/types/database/index.d.ts +0 -66
  95. package/types/debug.d.ts +0 -1
  96. package/types/env/index.d.ts +0 -144
  97. package/types/environment.d.ts +0 -51
  98. package/types/environmentManager.d.ts +0 -13
  99. package/types/error.d.ts +0 -18
  100. package/types/function/index.d.ts +0 -379
  101. package/types/function/packer.d.ts +0 -37
  102. package/types/function/types.d.ts +0 -156
  103. package/types/hosting/index.d.ts +0 -253
  104. package/types/index.d.ts +0 -52
  105. package/types/interfaces/base.interface.d.ts +0 -7
  106. package/types/interfaces/billing.interface.d.ts +0 -18
  107. package/types/interfaces/cam.interface.d.ts +0 -24
  108. package/types/interfaces/flexdb.interface.d.ts +0 -67
  109. package/types/interfaces/function.interface.d.ts +0 -65
  110. package/types/interfaces/index.d.ts +0 -7
  111. package/types/interfaces/storage.interface.d.ts +0 -26
  112. package/types/interfaces/tcb.interface.d.ts +0 -305
  113. package/types/storage/index.d.ts +0 -324
  114. package/types/third/index.d.ts +0 -11
  115. package/types/user/index.d.ts +0 -52
  116. package/types/user/types.d.ts +0 -20
  117. package/types/utils/auth.d.ts +0 -8
  118. package/types/utils/cloud-api-request.d.ts +0 -21
  119. package/types/utils/cloudbase-request.d.ts +0 -14
  120. package/types/utils/envLazy.d.ts +0 -1
  121. package/types/utils/fs.d.ts +0 -7
  122. package/types/utils/http-request.d.ts +0 -2
  123. package/types/utils/index.d.ts +0 -20
  124. package/types/utils/parallel.d.ts +0 -17
  125. package/types/utils/uuid.d.ts +0 -2
@@ -1,1035 +0,0 @@
1
- "use strict";
2
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
- return c > 3 && r && Object.defineProperty(target, key, r), r;
7
- };
8
- var __importDefault = (this && this.__importDefault) || function (mod) {
9
- return (mod && mod.__esModule) ? mod : { "default": mod };
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.StorageService = void 0;
13
- const fs_1 = __importDefault(require("fs"));
14
- const util_1 = __importDefault(require("util"));
15
- const path_1 = __importDefault(require("path"));
16
- const make_dir_1 = __importDefault(require("make-dir"));
17
- const walkdir_1 = __importDefault(require("walkdir"));
18
- const micromatch_1 = __importDefault(require("micromatch"));
19
- const cos_nodejs_sdk_v5_1 = __importDefault(require("cos-nodejs-sdk-v5"));
20
- const utils_1 = require("../utils");
21
- const error_1 = require("../error");
22
- const parallel_1 = require("../utils/parallel");
23
- const BIG_FILE_SIZE = 5242880; // 5MB 1024*1024*5
24
- class StorageService {
25
- constructor(environment) {
26
- this.environment = environment;
27
- this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
28
- }
29
- /**
30
- * 上传文件
31
- * localPath 为文件夹时,会尝试在文件夹中寻找 cloudPath 中的文件名
32
- * @param {string} localPath 本地文件的绝对路径
33
- * @param {string} cloudPath 云端文件路径,如 img/test.png
34
- * @returns {Promise<any>}
35
- */
36
- async uploadFile(options) {
37
- const { localPath, cloudPath = '', onProgress } = options;
38
- const { bucket, region } = this.getStorageConfig();
39
- return this.uploadFileCustom({
40
- localPath,
41
- cloudPath,
42
- bucket,
43
- region,
44
- onProgress
45
- });
46
- }
47
- /**
48
- * 批量上传文件,默认并发 5
49
- * @param options
50
- */
51
- async uploadFiles(options) {
52
- const { files, onProgress, parallel, onFileFinish, ignore, retryCount, retryInterval } = options;
53
- const { bucket, region } = this.getStorageConfig();
54
- return this.uploadFilesCustom({
55
- files,
56
- bucket,
57
- region,
58
- ignore,
59
- parallel,
60
- onProgress,
61
- onFileFinish,
62
- retryCount,
63
- retryInterval
64
- });
65
- }
66
- /**
67
- * 上传文件,支持自定义 Bucket 和 Region
68
- * @param {string} localPath
69
- * @param {string} cloudPath
70
- * @param {string} bucket
71
- * @param {string} region
72
- */
73
- async uploadFileCustom(options) {
74
- const { localPath, cloudPath, bucket, region, onProgress, fileId = true } = options;
75
- let localFilePath = '';
76
- let resolveLocalPath = path_1.default.resolve(localPath);
77
- utils_1.checkFullAccess(resolveLocalPath, true);
78
- // 如果 localPath 是一个文件夹,尝试在文件下寻找 cloudPath 中的文件
79
- const fileStats = fs_1.default.statSync(resolveLocalPath);
80
- if (fileStats.isDirectory()) {
81
- const fileName = path_1.default.parse(cloudPath).base;
82
- const attemptFilePath = path_1.default.join(localPath, fileName);
83
- if (utils_1.checkFullAccess(attemptFilePath)) {
84
- localFilePath = path_1.default.resolve(attemptFilePath);
85
- }
86
- }
87
- else {
88
- localFilePath = resolveLocalPath;
89
- }
90
- if (!localFilePath) {
91
- throw new error_1.CloudBaseError('本地文件不存在!');
92
- }
93
- const cos = this.getCos();
94
- const putObject = util_1.default.promisify(cos.putObject).bind(cos);
95
- const sliceUploadFile = util_1.default.promisify(cos.sliceUploadFile).bind(cos);
96
- let cosFileId;
97
- // 针对静态托管,fileId 不是必须的
98
- if (fileId) {
99
- // 针对文件存储,cosFileId 是必须的,区分上传人员,否则无法获取下载连接
100
- const res = await this.getUploadMetadata(cloudPath);
101
- cosFileId = res.cosFileId;
102
- }
103
- let res;
104
- // 小文件,直接上传
105
- if (fileStats.size < BIG_FILE_SIZE) {
106
- res = await putObject({
107
- onProgress,
108
- Bucket: bucket,
109
- Region: region,
110
- Key: cloudPath,
111
- StorageClass: 'STANDARD',
112
- ContentLength: fileStats.size,
113
- Body: fs_1.default.createReadStream(localFilePath),
114
- 'x-cos-meta-fileid': cosFileId
115
- });
116
- }
117
- else {
118
- // 大文件,分块上传
119
- res = await sliceUploadFile({
120
- Bucket: bucket,
121
- Region: region,
122
- Key: cloudPath,
123
- FilePath: localFilePath,
124
- StorageClass: 'STANDARD',
125
- AsyncLimit: 3,
126
- onProgress,
127
- 'x-cos-meta-fileid': cosFileId
128
- });
129
- }
130
- if (res.statusCode !== 200) {
131
- throw new error_1.CloudBaseError(`上传文件错误:${JSON.stringify(res)}`);
132
- }
133
- return res;
134
- }
135
- /**
136
- * 上传文件夹
137
- * @param {string} localPath 本地文件夹路径
138
- * @param {string} cloudPath 云端文件夹
139
- * @param {number} parallel 并发量
140
- * @param {number} retryCount 重试次数
141
- * @param {number} retryInterval 重试时间间隔(毫秒)
142
- * @param {(string | string[])} ignore
143
- * @param {(string | string[])} ignore
144
- * @returns {Promise<void>}
145
- */
146
- async uploadDirectory(options) {
147
- const { localPath, cloudPath = '', ignore, onProgress, onFileFinish, parallel, retryCount, retryInterval } = options;
148
- // 此处不检查路径是否存在
149
- // 绝对路径 /var/blog/xxxx
150
- const { bucket, region } = this.getStorageConfig();
151
- return this.uploadDirectoryCustom({
152
- localPath,
153
- cloudPath,
154
- parallel,
155
- retryCount,
156
- retryInterval,
157
- bucket,
158
- region,
159
- ignore,
160
- onProgress,
161
- onFileFinish
162
- });
163
- }
164
- /**
165
- * 上传文件夹,支持自定义 Region 和 Bucket
166
- * @param {string} localPath
167
- * @param {string} cloudPath
168
- * @param {number} parallel
169
- * @param {number} retryCount
170
- * @param {number} retryInterval
171
- * @param {string} bucket
172
- * @param {string} region
173
- * @param {IOptions} options
174
- * @returns {Promise<void>}
175
- */
176
- async uploadDirectoryCustom(options) {
177
- const { localPath, cloudPath, bucket, region, onProgress, onFileFinish, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
178
- // 此处不检查路径是否存在
179
- // 绝对路径 /var/blog/xxxx
180
- const resolvePath = path_1.default.resolve(localPath);
181
- // 在路径结尾加上 '/'
182
- const resolveLocalPath = path_1.default.join(resolvePath, path_1.default.sep);
183
- const filePaths = await this.walkLocalDir(resolveLocalPath, ignore);
184
- if (!filePaths || !filePaths.length) {
185
- return;
186
- }
187
- const fileStatsList = filePaths.map(filePath => {
188
- // 处理 windows 路径
189
- const fileKeyPath = filePath.replace(resolveLocalPath, '').replace(/\\/g, '/');
190
- // 解析 cloudPath
191
- let cloudFileKey = path_1.default.join(cloudPath, fileKeyPath).replace(/\\/g, '/');
192
- if (utils_1.isDirectory(filePath)) {
193
- cloudFileKey = this.getCloudKey(cloudFileKey);
194
- return {
195
- filePath,
196
- cloudFileKey,
197
- isDir: true
198
- };
199
- }
200
- else {
201
- return {
202
- filePath,
203
- cloudFileKey,
204
- isDir: false
205
- };
206
- }
207
- });
208
- // 创建目录请求
209
- const creatingDirController = new parallel_1.AsyncTaskParallelController(parallel, 50);
210
- const creatingDirTasks = fileStatsList
211
- .filter(info => info.isDir)
212
- .map(info => () => this.createCloudDirectroyCustom({
213
- cloudPath: info.cloudFileKey,
214
- bucket,
215
- region
216
- }));
217
- creatingDirController.loadTasks(creatingDirTasks);
218
- await creatingDirController.run();
219
- // 上传文件对象
220
- const tasks = fileStatsList
221
- .filter(stats => !stats.isDir)
222
- .map(stats => async () => {
223
- let cosFileId;
224
- if (fileId) {
225
- const res = await this.getUploadMetadata(stats.cloudFileKey);
226
- cosFileId = res.cosFileId;
227
- }
228
- return {
229
- Bucket: bucket,
230
- Region: region,
231
- Key: stats.cloudFileKey,
232
- FilePath: stats.filePath,
233
- 'x-cos-meta-fileid': cosFileId
234
- };
235
- });
236
- // 控制请求并发
237
- const getMetadataController = new parallel_1.AsyncTaskParallelController(parallel, 50);
238
- getMetadataController.loadTasks(tasks);
239
- const files = await getMetadataController.run();
240
- // 对文件上传进行处理
241
- const cos = this.getCos(parallel);
242
- const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
243
- const params = {
244
- files,
245
- SliceSize: BIG_FILE_SIZE,
246
- onProgress,
247
- onFileFinish
248
- };
249
- return this.uploadFilesWithRetry({
250
- uploadFiles,
251
- options: params,
252
- times: retryCount,
253
- interval: retryInterval,
254
- failedFiles: []
255
- });
256
- }
257
- /**
258
- * 批量上传文件
259
- * @param options
260
- */
261
- async uploadFilesCustom(options) {
262
- const { files, bucket, region, ignore, onProgress, onFileFinish, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
263
- if (!files || !files.length) {
264
- return;
265
- }
266
- let fileList = files
267
- .map(item => {
268
- const { localPath, cloudPath } = item;
269
- return {
270
- filePath: localPath,
271
- cloudFileKey: cloudPath
272
- };
273
- })
274
- .filter(item => ((ignore === null || ignore === void 0 ? void 0 : ignore.length) ? !micromatch_1.default.isMatch(item.filePath, ignore) : true));
275
- // 生成上传文件属性
276
- const tasks = fileList.map(stats => async () => {
277
- let cosFileId;
278
- if (fileId) {
279
- const res = await this.getUploadMetadata(stats.cloudFileKey);
280
- cosFileId = res.cosFileId;
281
- }
282
- return {
283
- Bucket: bucket,
284
- Region: region,
285
- Key: stats.cloudFileKey,
286
- FilePath: stats.filePath,
287
- 'x-cos-meta-fileid': cosFileId
288
- };
289
- });
290
- // 控制请求并发
291
- const asyncTaskController = new parallel_1.AsyncTaskParallelController(parallel, 50);
292
- asyncTaskController.loadTasks(tasks);
293
- fileList = await asyncTaskController.run();
294
- const cos = this.getCos(parallel);
295
- const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
296
- const params = {
297
- files: fileList,
298
- SliceSize: BIG_FILE_SIZE,
299
- onProgress,
300
- onFileFinish
301
- };
302
- // return uploadFiles({
303
- // onProgress,
304
- // onFileFinish,
305
- // files: fileList,
306
- // SliceSize: BIG_FILE_SIZE
307
- // })
308
- return this.uploadFilesWithRetry({
309
- uploadFiles,
310
- options: params,
311
- times: retryCount,
312
- interval: retryInterval,
313
- failedFiles: []
314
- });
315
- }
316
- /**
317
- * 创建一个空的文件夹
318
- * @param {string} cloudPath
319
- */
320
- async createCloudDirectroy(cloudPath) {
321
- const { bucket, region } = this.getStorageConfig();
322
- await this.createCloudDirectroyCustom({
323
- cloudPath,
324
- bucket,
325
- region
326
- });
327
- }
328
- /**
329
- * 创建一个空的文件夹,支持自定义 Region 和 Bucket
330
- * @param {string} cloudPath
331
- * @param {string} bucket
332
- * @param {string} region
333
- */
334
- async createCloudDirectroyCustom(options) {
335
- const { cloudPath, bucket, region } = options;
336
- const cos = this.getCos();
337
- const putObject = util_1.default.promisify(cos.putObject).bind(cos);
338
- const dirKey = this.getCloudKey(cloudPath);
339
- const res = await putObject({
340
- Bucket: bucket,
341
- Region: region,
342
- Key: dirKey,
343
- Body: ''
344
- });
345
- if (res.statusCode !== 200) {
346
- throw new error_1.CloudBaseError(`创建文件夹失败:${JSON.stringify(res)}`);
347
- }
348
- }
349
- /**
350
- * 下载文件
351
- * @param {string} cloudPath 云端文件路径
352
- * @param {string} localPath 文件本地存储路径,文件需指定文件名称
353
- * @returns {Promise<NodeJS.ReadableStream>}
354
- */
355
- async downloadFile(options) {
356
- const { cloudPath, localPath } = options;
357
- const resolveLocalPath = path_1.default.resolve(localPath);
358
- const fileDir = path_1.default.dirname(localPath);
359
- utils_1.checkFullAccess(fileDir, true);
360
- const urlList = await this.getTemporaryUrl([cloudPath]);
361
- const { url } = urlList[0];
362
- const { proxy } = await this.environment.getAuthConfig();
363
- const res = await utils_1.fetchStream(url, {}, proxy);
364
- // localPath 不存在时,返回 ReadableStream
365
- if (!localPath) {
366
- return res.body;
367
- }
368
- const dest = fs_1.default.createWriteStream(resolveLocalPath);
369
- res.body.pipe(dest);
370
- // 写完成后返回
371
- return new Promise(resolve => {
372
- dest.on('close', () => {
373
- // 返回文件地址
374
- resolve(resolveLocalPath);
375
- });
376
- });
377
- }
378
- /**
379
- * 下载文件夹
380
- * @param {string} cloudPath 云端文件路径
381
- * @param {string} localPath 本地文件夹存储路径
382
- * @returns {Promise<(NodeJS.ReadableStream | string)[]>}
383
- */
384
- async downloadDirectory(options) {
385
- const { cloudPath, localPath, parallel = 20 } = options;
386
- const resolveLocalPath = path_1.default.resolve(localPath);
387
- utils_1.checkFullAccess(resolveLocalPath, true);
388
- const cloudDirectoryKey = this.getCloudKey(cloudPath);
389
- const files = await this.walkCloudDir(cloudDirectoryKey);
390
- const promises = files.map(file => async () => {
391
- return this.downloadWithFilePath({ file, cloudDirectoryKey, resolveLocalPath });
392
- });
393
- const asyncTaskController = new parallel_1.AsyncTaskParallelController(parallel, 50);
394
- asyncTaskController.loadTasks(promises);
395
- let res = await asyncTaskController.run();
396
- const errorIndexArr = [];
397
- res.map((item, index) => /Error/gi.test(Object.prototype.toString.call(item)) && errorIndexArr.push(index));
398
- // 重试逻辑
399
- if (errorIndexArr.length) {
400
- const errorFiles = errorIndexArr.map(errorIndex => files[errorIndex]);
401
- asyncTaskController.loadTasks(errorFiles.map(file => async () => {
402
- return this.downloadWithFilePath({ file, cloudDirectoryKey, resolveLocalPath });
403
- }));
404
- res = await asyncTaskController.run();
405
- }
406
- const errorResultArr = this.determineDownLoadResultIsError(res);
407
- if (errorResultArr.length) {
408
- throw errorResultArr[0];
409
- }
410
- return res;
411
- }
412
- /**
413
- * 列出文件夹下的文件
414
- * @link https://cloud.tencent.com/document/product/436/7734
415
- * @param {string} cloudPath 云端文件夹,如果为空字符串,则表示根目录
416
- * @returns {Promise<ListFileInfo[]>}
417
- */
418
- async listDirectoryFiles(cloudPath) {
419
- return this.walkCloudDir(cloudPath);
420
- }
421
- /**
422
- * 获取文件临时下载链接
423
- * @param {((string | ITempUrlInfo)[])} fileList 文件路径或文件信息数组
424
- * @returns {Promise<{ fileId: string; url: string }[]>}
425
- */
426
- async getTemporaryUrl(fileList) {
427
- if (!fileList || !Array.isArray(fileList)) {
428
- throw new error_1.CloudBaseError('fileList 必须是非空的数组');
429
- }
430
- const files = fileList.map(item => {
431
- if (typeof item === 'string') {
432
- return { cloudPath: item, maxAge: 3600 };
433
- }
434
- else {
435
- return item;
436
- }
437
- });
438
- const invalidData = files.find(item => !item.cloudPath || !item.maxAge || typeof item.cloudPath !== 'string');
439
- if (invalidData) {
440
- throw new error_1.CloudBaseError(`非法参数:${JSON.stringify(invalidData)}`);
441
- }
442
- const notExistsFiles = [];
443
- const checkFileRequests = files.map(file => (async () => {
444
- try {
445
- await this.getFileInfo(file.cloudPath);
446
- }
447
- catch (e) {
448
- if (e.statusCode === 404) {
449
- notExistsFiles.push(file.cloudPath);
450
- }
451
- }
452
- })());
453
- await Promise.all(checkFileRequests);
454
- // 文件路径不存在
455
- if (notExistsFiles.length) {
456
- throw new error_1.CloudBaseError(`以下文件不存在:${notExistsFiles.join(', ')}`);
457
- }
458
- const data = files.map(item => ({
459
- fileid: this.cloudPathToFileId(item.cloudPath),
460
- max_age: item.maxAge
461
- }));
462
- const config = this.environment.getAuthConfig();
463
- const res = await utils_1.cloudBaseRequest({
464
- config,
465
- params: {
466
- file_list: data,
467
- action: 'storage.batchGetDownloadUrl'
468
- },
469
- method: 'POST'
470
- });
471
- const downloadList = res.data.download_list.map(item => ({
472
- url: item.download_url,
473
- fileId: item.fileid || item.fileID
474
- }));
475
- return downloadList;
476
- }
477
- /**
478
- * 删除文件
479
- * @param {string[]} cloudPathList 云端文件路径数组
480
- * @returns {Promise<void>}
481
- */
482
- async deleteFile(cloudPathList) {
483
- if (!cloudPathList || !Array.isArray(cloudPathList)) {
484
- throw new error_1.CloudBaseError('fileList必须是非空的数组');
485
- }
486
- const hasInvalidFileId = cloudPathList.some(file => !file || typeof file !== 'string');
487
- if (hasInvalidFileId) {
488
- throw new error_1.CloudBaseError('fileList的元素必须是非空的字符串');
489
- }
490
- const { bucket, env } = this.getStorageConfig();
491
- const fileIdList = cloudPathList.map(filePath => this.cloudPathToFileId(filePath));
492
- const config = this.environment.getAuthConfig();
493
- const res = await utils_1.cloudBaseRequest({
494
- config,
495
- params: {
496
- action: 'storage.batchDeleteFile',
497
- fileid_list: fileIdList
498
- },
499
- method: 'POST'
500
- });
501
- const failedList = res.data.delete_list
502
- .filter(item => item.code !== 'SUCCESS')
503
- .map(item => `${item.fileID} : ${item.code}`);
504
- if (failedList.length) {
505
- throw new error_1.CloudBaseError(`部分删除文件失败:${JSON.stringify(failedList)}`);
506
- }
507
- }
508
- /**
509
- * 删除文件,可以指定 Bucket 和 Region
510
- * @param {string[]} cloudPathList
511
- * @param {string} bucket
512
- * @param {string} region
513
- * @returns {Promise<void>}
514
- */
515
- async deleteFileCustom(cloudPathList, bucket, region) {
516
- if (!cloudPathList || !Array.isArray(cloudPathList)) {
517
- throw new error_1.CloudBaseError('fileList必须是非空的数组');
518
- }
519
- const hasInvalidFileId = cloudPathList.some(file => !file || typeof file !== 'string');
520
- if (hasInvalidFileId) {
521
- throw new error_1.CloudBaseError('fileList的元素必须是非空的字符串');
522
- }
523
- const cos = this.getCos();
524
- const deleteObject = util_1.default.promisify(cos.deleteObject).bind(cos);
525
- const promises = cloudPathList.map(async (file) => deleteObject({
526
- Bucket: bucket,
527
- Region: region,
528
- Key: file
529
- }));
530
- await Promise.all(promises);
531
- }
532
- /**
533
- * 获取文件信息
534
- * @param {string} cloudPath 云端文件路径
535
- * @returns {Promise<FileInfo>}
536
- */
537
- async getFileInfo(cloudPath) {
538
- const cos = this.getCos();
539
- const headObject = util_1.default.promisify(cos.headObject).bind(cos);
540
- const { bucket, region } = this.getStorageConfig();
541
- const { headers } = await headObject({
542
- Bucket: bucket,
543
- Region: region,
544
- Key: cloudPath
545
- });
546
- if (!headers) {
547
- throw new error_1.CloudBaseError(`[${cloudPath}] 获取文件信息失败`);
548
- }
549
- // 文件大小 KB
550
- const size = Number(Number(headers['content-length']) / 1024).toFixed(2);
551
- return {
552
- Size: size,
553
- Type: headers['content-type'],
554
- Date: headers['date'],
555
- ETag: headers['etag']
556
- };
557
- }
558
- /**
559
- * 删除文件夹
560
- * @param {string} cloudPath 云端文件夹路径
561
- * @returns {Promise<void>}
562
- */
563
- async deleteDirectory(cloudPath) {
564
- const { bucket, region } = this.getStorageConfig();
565
- return this.deleteDirectoryCustom({
566
- cloudPath,
567
- bucket,
568
- region
569
- });
570
- }
571
- /**
572
- * 删除文件,可以指定 bucket 和 region
573
- * @param {string} cloudPath
574
- * @param {string} bucket
575
- * @param {string} region
576
- * @returns {Promise<void>}
577
- */
578
- async deleteDirectoryCustom(options) {
579
- const { cloudPath, bucket, region } = options;
580
- const key = this.getCloudKey(cloudPath);
581
- const cos = this.getCos();
582
- const deleteMultipleObject = util_1.default.promisify(cos.deleteMultipleObject).bind(cos);
583
- // 遍历获取全部文件
584
- const files = await this.walkCloudDirCustom({
585
- bucket,
586
- region,
587
- prefix: key
588
- });
589
- // 文件为空时,不能调用删除接口
590
- if (!files.length) {
591
- return {
592
- Deleted: [],
593
- Error: []
594
- };
595
- }
596
- // COS 接口最大一次删除 1000 个 Key
597
- // 将数组切分为 500 个文件一组
598
- const sliceGroup = [];
599
- const total = Math.ceil(files.length / 500);
600
- for (let i = 0; i < total; i++) {
601
- sliceGroup.push(files.splice(0, 500));
602
- }
603
- const tasks = sliceGroup.map(group => deleteMultipleObject({
604
- Bucket: bucket,
605
- Region: region,
606
- Objects: group.map(file => ({ Key: file.Key }))
607
- }));
608
- // 删除多个文件
609
- const taskRes = await Promise.all(tasks);
610
- // 合并响应结果
611
- const Deleted = taskRes.map(_ => _.Deleted).reduce((prev, next) => [...prev, ...next], []);
612
- const Error = taskRes.map(_ => _.Error).reduce((prev, next) => [...prev, ...next], []);
613
- return {
614
- Deleted,
615
- Error
616
- };
617
- }
618
- /**
619
- * 获取文件存储权限
620
- * READONLY:所有用户可读,仅创建者和管理员可写
621
- * PRIVATE:仅创建者及管理员可读写
622
- * ADMINWRITE:所有用户可读,仅管理员可写
623
- * ADMINONLY:仅管理员可读写
624
- * @returns
625
- */
626
- async getStorageAcl() {
627
- const { bucket, env } = this.getStorageConfig();
628
- const res = await this.tcbService.request('DescribeStorageACL', {
629
- EnvId: env,
630
- Bucket: bucket
631
- });
632
- return res.AclTag;
633
- }
634
- /**
635
- * 设置文件存储权限
636
- * READONLY:所有用户可读,仅创建者和管理员可写
637
- * PRIVATE:仅创建者及管理员可读写
638
- * ADMINWRITE:所有用户可读,仅管理员可写
639
- * ADMINONLY:仅管理员可读写
640
- * @param {string} acl
641
- * @returns
642
- */
643
- async setStorageAcl(acl) {
644
- const validAcl = ['READONLY', 'PRIVATE', 'ADMINWRITE', 'ADMINONLY'];
645
- if (!validAcl.includes(acl)) {
646
- throw new error_1.CloudBaseError('非法的权限类型');
647
- }
648
- const { bucket, env } = this.getStorageConfig();
649
- return this.tcbService.request('ModifyStorageACL', {
650
- EnvId: env,
651
- Bucket: bucket,
652
- AclTag: acl
653
- });
654
- }
655
- /**
656
- * 遍历云端文件夹
657
- * @param {string} prefix
658
- * @param {string} [marker] 路径开始标志
659
- * @returns {Promise<IListFileInfo[]>}
660
- */
661
- async walkCloudDir(prefix, marker) {
662
- const { bucket, region } = this.getStorageConfig();
663
- return this.walkCloudDirCustom({
664
- prefix,
665
- bucket,
666
- region,
667
- marker
668
- });
669
- }
670
- /**
671
- * 遍历云端文件夹,支持自定义 Bucket 和 Region
672
- * @param {string} prefix
673
- * @param {string} [marker]
674
- * @param {string} bucket
675
- * @param {string} region
676
- * @returns {Promise<IListFileInfo[]>}
677
- */
678
- async walkCloudDirCustom(options) {
679
- const { prefix, bucket, region, marker = '/' } = options;
680
- let fileList = [];
681
- const cos = this.getCos();
682
- const getBucket = util_1.default.promisify(cos.getBucket).bind(cos);
683
- const prefixKey = this.getCloudKey(prefix);
684
- const res = await getBucket({
685
- Bucket: bucket,
686
- Region: region,
687
- Prefix: prefixKey,
688
- MaxKeys: 100,
689
- Marker: marker
690
- });
691
- fileList.push(...res.Contents);
692
- let moreFiles = [];
693
- if (res.IsTruncated === 'true' || res.IsTruncated === true) {
694
- moreFiles = await this.walkCloudDirCustom({
695
- bucket,
696
- region,
697
- prefix: prefixKey,
698
- marker: res.NextMarker
699
- });
700
- }
701
- fileList.push(...moreFiles);
702
- return fileList;
703
- }
704
- /**
705
- * 遍历本地文件夹
706
- * 忽略不包含 dir 路径,即如果 ignore 匹配 dir,dir 也不会被忽略
707
- * @private
708
- * @param {string} dir
709
- * @param {(string | string[])} [ignore]
710
- * @returns
711
- */
712
- async walkLocalDir(dir, ignore) {
713
- try {
714
- return walkdir_1.default.async(dir, {
715
- filter: (currDir, files) => {
716
- // NOTE: ignore 为空数组时会忽略全部文件
717
- if (!ignore || !ignore.length)
718
- return files;
719
- return files.filter(item => {
720
- // 当前文件全路径
721
- const fullPath = path_1.default.join(currDir, item);
722
- // 文件相对于传入目录的路径
723
- const fileRelativePath = fullPath.replace(path_1.default.join(dir, path_1.default.sep), '');
724
- // 匹配
725
- return !micromatch_1.default.isMatch(fileRelativePath, ignore);
726
- });
727
- }
728
- });
729
- }
730
- catch (e) {
731
- throw new error_1.CloudBaseError(e.message);
732
- }
733
- }
734
- /**
735
- * 获取文件上传链接属性
736
- */
737
- async getUploadMetadata(path) {
738
- const config = this.environment.getAuthConfig();
739
- const res = await utils_1.cloudBaseRequest({
740
- config,
741
- params: {
742
- path,
743
- action: 'storage.getUploadMetadata'
744
- },
745
- method: 'POST'
746
- });
747
- if (res.code) {
748
- throw new error_1.CloudBaseError(`${res.code}: ${res.message || ''}`, {
749
- requestId: res.requestId
750
- });
751
- }
752
- return res.data;
753
- }
754
- /**
755
- * 获取静态网站配置
756
- */
757
- async getWebsiteConfig(options) {
758
- const { bucket, region } = options;
759
- const cos = this.getCos();
760
- const getBucketWebsite = util_1.default.promisify(cos.getBucketWebsite).bind(cos);
761
- const res = await getBucketWebsite({
762
- Bucket: bucket,
763
- Region: region
764
- });
765
- return res;
766
- }
767
- /**
768
- * 配置文档
769
- */
770
- async putBucketWebsite(options) {
771
- const { indexDocument, errorDocument, bucket, region, routingRules } = options;
772
- const cos = this.getCos();
773
- const putBucketWebsite = util_1.default.promisify(cos.putBucketWebsite).bind(cos);
774
- let params = {
775
- Bucket: bucket,
776
- Region: region,
777
- WebsiteConfiguration: {
778
- IndexDocument: {
779
- Suffix: indexDocument
780
- },
781
- ErrorDocument: {
782
- Key: errorDocument
783
- }
784
- }
785
- };
786
- if (routingRules) {
787
- params.WebsiteConfiguration.RoutingRules = [];
788
- for (let value of routingRules) {
789
- const routeItem = {};
790
- if (value.keyPrefixEquals) {
791
- routeItem.Condition = {
792
- KeyPrefixEquals: value.keyPrefixEquals
793
- };
794
- }
795
- if (value.httpErrorCodeReturnedEquals) {
796
- routeItem.Condition = {
797
- HttpErrorCodeReturnedEquals: value.httpErrorCodeReturnedEquals
798
- };
799
- }
800
- if (value.replaceKeyWith) {
801
- routeItem.Redirect = {
802
- ReplaceKeyWith: value.replaceKeyWith
803
- };
804
- }
805
- if (value.replaceKeyPrefixWith) {
806
- routeItem.Redirect = {
807
- ReplaceKeyPrefixWith: value.replaceKeyPrefixWith
808
- };
809
- }
810
- params.WebsiteConfiguration.RoutingRules.push(routeItem);
811
- }
812
- }
813
- const res = await putBucketWebsite(params);
814
- return res;
815
- }
816
- /**
817
- * 查询object列表
818
- * @param {IGetBucketOpions} options
819
- * @memberof StorageService
820
- */
821
- async getBucket(options) {
822
- // const { bucket } = this.getStorageConfig()
823
- const { prefix, maxKeys, marker, bucket, region } = options;
824
- const cos = this.getCos();
825
- const getBucket = util_1.default.promisify(cos.getBucket).bind(cos);
826
- const prefixKey = this.getCloudKey(prefix);
827
- const res = await getBucket({
828
- Bucket: bucket,
829
- Region: region,
830
- Prefix: prefixKey,
831
- MaxKeys: maxKeys,
832
- Marker: marker
833
- });
834
- return res;
835
- }
836
- /**
837
- * 获取 COS 配置
838
- */
839
- getCos(parallel = 20) {
840
- const { secretId, secretKey, token, proxy } = this.environment.getAuthConfig();
841
- const cosProxy = process.env.TCB_COS_PROXY;
842
- return new cos_nodejs_sdk_v5_1.default({
843
- FileParallelLimit: parallel,
844
- SecretId: secretId,
845
- SecretKey: secretKey,
846
- Proxy: cosProxy || proxy,
847
- SecurityToken: token
848
- });
849
- }
850
- /**
851
- * 将 cloudPath 转换成 cloudPath/ 形式
852
- */
853
- getCloudKey(cloudPath) {
854
- if (!cloudPath) {
855
- return '';
856
- }
857
- // 单个 / 转换成根目录
858
- if (cloudPath === '/') {
859
- return '';
860
- }
861
- return cloudPath[cloudPath.length - 1] === '/' ? cloudPath : `${cloudPath}/`;
862
- }
863
- /**
864
- * 将 cloudPath 转换成 fileId
865
- */
866
- cloudPathToFileId(cloudPath) {
867
- const { env, bucket } = this.getStorageConfig();
868
- return `cloud://${env}.${bucket}/${cloudPath}`;
869
- }
870
- /**
871
- * 获取存储桶配置
872
- */
873
- getStorageConfig() {
874
- var _a;
875
- const envConfig = this.environment.lazyEnvironmentConfig;
876
- const storageConfig = (_a = envConfig === null || envConfig === void 0 ? void 0 : envConfig.Storages) === null || _a === void 0 ? void 0 : _a[0];
877
- const { Region, Bucket } = storageConfig;
878
- const region = process.env.TCB_COS_REGION || Region;
879
- return {
880
- region,
881
- bucket: Bucket,
882
- env: envConfig.EnvId
883
- };
884
- }
885
- /**
886
- * 带重试功能的上传多文件函数
887
- * @param uploadFiles sdk上传函数
888
- * @param options sdk上传函数参数
889
- * @param times 重试次数
890
- * @param interval 重试时间间隔(毫秒)
891
- * @param failedFiles 失败文件列表
892
- * @returns
893
- */
894
- async uploadFilesWithRetry({ uploadFiles, options, times, interval, failedFiles }) {
895
- const { files, onFileFinish } = options;
896
- const tempFailedFiles = [];
897
- let curError = null;
898
- const res = await uploadFiles(Object.assign(Object.assign({}, options), { files: failedFiles.length
899
- ? files.filter(file => failedFiles.includes(file.Key))
900
- : files, onFileFinish: (...args) => {
901
- const error = args[0];
902
- const fileInfo = args[2];
903
- if (error) {
904
- curError = error;
905
- tempFailedFiles.push(fileInfo.Key);
906
- }
907
- onFileFinish === null || onFileFinish === void 0 ? void 0 : onFileFinish.apply(null, args);
908
- } }));
909
- // if (!tempFailedFiles?.length || times <= 0) return res
910
- if (!(tempFailedFiles === null || tempFailedFiles === void 0 ? void 0 : tempFailedFiles.length)) {
911
- return res;
912
- }
913
- else {
914
- if (times > 0) {
915
- return await new Promise((resolve, reject) => {
916
- setTimeout(() => this.uploadFilesWithRetry({
917
- uploadFiles,
918
- options,
919
- times: times - 1,
920
- interval,
921
- failedFiles: tempFailedFiles
922
- }).then(res => resolve(res))
923
- .catch(err => reject(err)), interval);
924
- });
925
- }
926
- else {
927
- if (curError) {
928
- throw curError;
929
- }
930
- }
931
- }
932
- }
933
- /**
934
- * 拼接路径下载单文件
935
- * @param file
936
- * @param cloudDirectoryKey
937
- * @param resolveLocalPath
938
- * @returns
939
- */
940
- async downloadWithFilePath({ file, cloudDirectoryKey, resolveLocalPath }) {
941
- const fileRelativePath = file.Key.replace(cloudDirectoryKey, '');
942
- // 空路径和文件夹跳过
943
- if (!fileRelativePath || /\/$/g.test(fileRelativePath)) {
944
- return;
945
- }
946
- const localFilePath = path_1.default.join(resolveLocalPath, fileRelativePath);
947
- // 创建文件的父文件夹
948
- const fileDir = path_1.default.dirname(localFilePath);
949
- await make_dir_1.default(fileDir);
950
- return this.downloadFile({
951
- cloudPath: file.Key,
952
- localPath: localFilePath
953
- });
954
- }
955
- /**
956
- * 根据下载结果返回错误列表
957
- * @param res
958
- * @returns
959
- */
960
- determineDownLoadResultIsError(res) {
961
- const resultErrorArr = [];
962
- res.map(item => /Error/gi.test(Object.prototype.toString.call(item)) && resultErrorArr.push(item));
963
- return resultErrorArr;
964
- }
965
- }
966
- __decorate([
967
- utils_1.preLazy()
968
- ], StorageService.prototype, "uploadFile", null);
969
- __decorate([
970
- utils_1.preLazy()
971
- ], StorageService.prototype, "uploadFiles", null);
972
- __decorate([
973
- utils_1.preLazy()
974
- ], StorageService.prototype, "uploadFileCustom", null);
975
- __decorate([
976
- utils_1.preLazy()
977
- ], StorageService.prototype, "uploadDirectory", null);
978
- __decorate([
979
- utils_1.preLazy()
980
- ], StorageService.prototype, "uploadDirectoryCustom", null);
981
- __decorate([
982
- utils_1.preLazy()
983
- ], StorageService.prototype, "uploadFilesCustom", null);
984
- __decorate([
985
- utils_1.preLazy()
986
- ], StorageService.prototype, "createCloudDirectroy", null);
987
- __decorate([
988
- utils_1.preLazy()
989
- ], StorageService.prototype, "createCloudDirectroyCustom", null);
990
- __decorate([
991
- utils_1.preLazy()
992
- ], StorageService.prototype, "downloadFile", null);
993
- __decorate([
994
- utils_1.preLazy()
995
- ], StorageService.prototype, "downloadDirectory", null);
996
- __decorate([
997
- utils_1.preLazy()
998
- ], StorageService.prototype, "listDirectoryFiles", null);
999
- __decorate([
1000
- utils_1.preLazy()
1001
- ], StorageService.prototype, "getTemporaryUrl", null);
1002
- __decorate([
1003
- utils_1.preLazy()
1004
- ], StorageService.prototype, "deleteFile", null);
1005
- __decorate([
1006
- utils_1.preLazy()
1007
- ], StorageService.prototype, "deleteFileCustom", null);
1008
- __decorate([
1009
- utils_1.preLazy()
1010
- ], StorageService.prototype, "getFileInfo", null);
1011
- __decorate([
1012
- utils_1.preLazy()
1013
- ], StorageService.prototype, "deleteDirectory", null);
1014
- __decorate([
1015
- utils_1.preLazy()
1016
- ], StorageService.prototype, "deleteDirectoryCustom", null);
1017
- __decorate([
1018
- utils_1.preLazy()
1019
- ], StorageService.prototype, "getStorageAcl", null);
1020
- __decorate([
1021
- utils_1.preLazy()
1022
- ], StorageService.prototype, "setStorageAcl", null);
1023
- __decorate([
1024
- utils_1.preLazy()
1025
- ], StorageService.prototype, "walkCloudDir", null);
1026
- __decorate([
1027
- utils_1.preLazy()
1028
- ], StorageService.prototype, "walkCloudDirCustom", null);
1029
- __decorate([
1030
- utils_1.preLazy()
1031
- ], StorageService.prototype, "putBucketWebsite", null);
1032
- __decorate([
1033
- utils_1.preLazy()
1034
- ], StorageService.prototype, "getBucket", null);
1035
- exports.StorageService = StorageService;