@cloudbase/manager-node 5.8.0-beta.2 → 5.8.1

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.
@@ -10,7 +10,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.StorageService = void 0;
13
+ exports.StorageService = exports.md5File = void 0;
14
14
  const fs_1 = __importDefault(require("fs"));
15
15
  const util_1 = __importDefault(require("util"));
16
16
  const path_1 = __importDefault(require("path"));
@@ -24,12 +24,418 @@ const error_1 = require("../error");
24
24
  const parallel_1 = require("../utils/parallel");
25
25
  const runenv_1 = require("../utils/runenv");
26
26
  const constant_1 = require("../constant");
27
+ const helper_1 = require("./helper");
27
28
  const BIG_FILE_SIZE = 5242880; // 5MB 1024*1024*5
29
+ // ===== hosting deploy consistency 相关能力 =====
30
+ /** 默认 MD5 计算并发数 */
31
+ const DEFAULT_MD5_PARALLEL = 20;
32
+ /** 一致性发布备份前缀(用于 diff.removed 排除备份文件) */
33
+ const CONSISTENT_DEPLOY_BACKUP_PREFIX = '.cloudbase-backup/';
34
+ var helper_2 = require("./helper");
35
+ Object.defineProperty(exports, "md5File", { enumerable: true, get: function () { return helper_2.md5File; } });
28
36
  class StorageService {
29
37
  constructor(environment) {
30
38
  this.environment = environment;
31
39
  this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
32
40
  }
41
+ // ==================== 一致性发布 相关能力 ====================
42
+ /**
43
+ * 获取远端 manifest(默认 Storage 桶):返回指定前缀下文件的 key / etag / size,
44
+ * 过滤目录占位对象。prefix 无前导斜杠,空串表示整桶(Hosting 桶请用 getRemoteManifestCustom)。
45
+ */
46
+ async getRemoteManifest(prefix) {
47
+ const { bucket, region } = this.getStorageConfig();
48
+ return this.getRemoteManifestCustom({
49
+ prefix,
50
+ bucket,
51
+ region
52
+ });
53
+ }
54
+ /**
55
+ * 获取远端 manifest,支持自定义 Bucket 和 Region。prefix 规则同 getRemoteManifest。
56
+ */
57
+ async getRemoteManifestCustom(options) {
58
+ const fileList = await this.walkCloudDirCustom(options);
59
+ return fileList
60
+ .filter(file => file.Key && !file.Key.endsWith('/'))
61
+ .map(file => ({
62
+ key: file.Key,
63
+ etag: String(file.ETag || '').replace(/^"|"$/g, ''),
64
+ size: Number(file.Size) || 0
65
+ }));
66
+ }
67
+ /**
68
+ * 生成本地 manifest:计算每个文件的云端 Key、MD5 与 Size。
69
+ * - filePaths 须来自 walkLocalDir(localPath),目录不计入
70
+ * - key = cloudPath + 文件相对路径;cloudPath 为空表示桶根
71
+ * - MD5 以受控并发计算,避免阻塞事件循环
72
+ */
73
+ async buildLocalManifest(options) {
74
+ const { localPath, cloudPath, filePaths, parallel = DEFAULT_MD5_PARALLEL } = options;
75
+ const resolvePath = path_1.default.resolve(localPath);
76
+ const resolveLocalPath = path_1.default.join(resolvePath, path_1.default.sep);
77
+ const controller = new parallel_1.AsyncTaskParallelController(parallel, 50);
78
+ controller.loadTasks(filePaths.map(filePath => async () => {
79
+ const stat = await fs_1.default.promises.stat(filePath);
80
+ if (stat.isDirectory()) {
81
+ return null;
82
+ }
83
+ const fileKeyPath = filePath.replace(resolveLocalPath, '').replace(/\\/g, '/');
84
+ const key = path_1.default.join(cloudPath || '', fileKeyPath).replace(/\\/g, '/');
85
+ return {
86
+ key,
87
+ md5: await (0, helper_1.md5File)(filePath),
88
+ size: stat.size,
89
+ localPath: filePath
90
+ };
91
+ }));
92
+ const results = await controller.run();
93
+ const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
94
+ const firstError = results.find(isError);
95
+ if (firstError) {
96
+ throw new error_1.CloudBaseError(`计算本地文件摘要失败:${firstError.message}`);
97
+ }
98
+ return results.filter(res => res !== null);
99
+ }
100
+ /**
101
+ * 计算本地 manifest 与远端 manifest 的差异:
102
+ * - added:本地有、远端无;overwritten:两边都有;removed:远端有、本地无
103
+ * - 两份 manifest 的 key 须基于同一 cloudPath(removed 自动排除 .cloudbase-backup/)
104
+ */
105
+ diffManifest(localManifest, remoteManifest) {
106
+ const localKeys = new Set(localManifest.map(item => item.key));
107
+ const remoteKeys = new Set(remoteManifest.map(item => item.key));
108
+ const added = [];
109
+ const overwritten = [];
110
+ localManifest.forEach(item => {
111
+ if (remoteKeys.has(item.key)) {
112
+ overwritten.push(item.key);
113
+ return;
114
+ }
115
+ added.push(item.key);
116
+ });
117
+ const removed = remoteManifest
118
+ .map(item => item.key)
119
+ .filter(key => !localKeys.has(key) &&
120
+ !key.replace(/^\/+/, '').startsWith(CONSISTENT_DEPLOY_BACKUP_PREFIX));
121
+ return {
122
+ added,
123
+ overwritten,
124
+ removed
125
+ };
126
+ }
127
+ /**
128
+ * 校验远端 manifest 与本地 manifest 是否一致:
129
+ * - 远端缺失 → missing;ETag 为纯 MD5 → 按 md5 比对;分块上传 ETag → 降级按 size 比对
130
+ */
131
+ verifyManifest(localManifest, remoteManifest) {
132
+ const remoteMap = new Map(remoteManifest.map(item => [item.key, item]));
133
+ const missing = [];
134
+ const mismatched = [];
135
+ const pureMd5EtagRe = /^[a-f0-9]{32}$/i;
136
+ localManifest.forEach(localItem => {
137
+ const remoteItem = remoteMap.get(localItem.key);
138
+ if (!remoteItem) {
139
+ missing.push(localItem.key);
140
+ return;
141
+ }
142
+ const remoteEtag = String(remoteItem.etag || '').replace(/^"|"$/g, '');
143
+ const shouldCompareByMd5 = pureMd5EtagRe.test(remoteEtag);
144
+ if (shouldCompareByMd5) {
145
+ if (remoteEtag.toLowerCase() !== localItem.md5.toLowerCase()) {
146
+ mismatched.push(localItem.key);
147
+ }
148
+ return;
149
+ }
150
+ if (Number(remoteItem.size) !== Number(localItem.size)) {
151
+ mismatched.push(localItem.key);
152
+ }
153
+ });
154
+ return {
155
+ ok: missing.length === 0 && mismatched.length === 0,
156
+ missing,
157
+ mismatched
158
+ };
159
+ }
160
+ /**
161
+ * 将指定前缀下的远端文件服务端复制到备份前缀(不产生下载流量)。
162
+ * 始终跳过 .cloudbase-backup/ 下的历史备份,避免根目录发布时递归备份。
163
+ */
164
+ async backupRemote(options) {
165
+ const { bucket, region, prefix, backupPrefix, parallel = 20 } = options;
166
+ // COS 对象 Key 不保留前导斜杠,统一规范化为 .cloudbase-backup/... 形式
167
+ const normalizedBackupPrefix = backupPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
168
+ const remoteManifest = await this.getRemoteManifestCustom({
169
+ bucket,
170
+ region,
171
+ prefix
172
+ });
173
+ // 过滤掉已备份的文件,避免根目录发布时递归备份旧备份,filesToBackup 存的是需要备份的文件
174
+ const filesToBackup = remoteManifest.filter(item => !item.key.replace(/^\/+/, '').startsWith(CONSISTENT_DEPLOY_BACKUP_PREFIX));
175
+ if (!filesToBackup.length) {
176
+ return;
177
+ }
178
+ const cos = this.getCos();
179
+ const putObjectCopy = util_1.default.promisify(cos.putObjectCopy).bind(cos);
180
+ const controller = new parallel_1.AsyncTaskParallelController(parallel, 50);
181
+ controller.loadTasks(
182
+ // 为每个文件创建一个复制任务
183
+ filesToBackup.map(file => async () => {
184
+ const destPath = `${normalizedBackupPrefix}/${file.key.replace(/^\/+/, '')}`;
185
+ // 拼接复制源
186
+ const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent(file.key)}`;
187
+ const res = await putObjectCopy({
188
+ Bucket: bucket,
189
+ Region: region,
190
+ Key: destPath,
191
+ CopySource: copySource
192
+ });
193
+ if (res.statusCode !== 200) {
194
+ throw new error_1.CloudBaseError(`备份文件失败:${file.key}`);
195
+ }
196
+ return destPath;
197
+ }));
198
+ const results = await controller.run();
199
+ const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
200
+ const firstError = results.find(isError);
201
+ if (firstError) {
202
+ throw new error_1.CloudBaseError(`远端备份失败:${firstError.message}`);
203
+ }
204
+ }
205
+ /**
206
+ * 从备份恢复被覆盖旧文件(服务端复制回原 Key),并删除本次新增文件。
207
+ * 任一阶段失败都会抛错,由上层决定是否保留备份供人工恢复。
208
+ */
209
+ async restoreFromBackup(options) {
210
+ const { bucket, region, backupPrefix, overwrittenKeys, addedKeys, parallel = 20 } = options;
211
+ // COS 对象 Key 不保留前导斜杠,与 backupRemote 的规范化规则保持一致
212
+ const normalizedBackupPrefix = backupPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
213
+ if (overwrittenKeys.length > 0) {
214
+ const cos = this.getCos();
215
+ const putObjectCopy = util_1.default.promisify(cos.putObjectCopy).bind(cos);
216
+ const controller = new parallel_1.AsyncTaskParallelController(parallel, 50);
217
+ controller.loadTasks(overwrittenKeys.map(key => async () => {
218
+ const sourcePath = `${normalizedBackupPrefix}/${key.replace(/^\/+/, '')}`;
219
+ const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent(sourcePath)}`;
220
+ const res = await putObjectCopy({
221
+ Bucket: bucket,
222
+ Region: region,
223
+ Key: key,
224
+ CopySource: copySource
225
+ });
226
+ if (res.statusCode !== 200) {
227
+ throw new error_1.CloudBaseError(`恢复备份文件失败:${key}`);
228
+ }
229
+ return key;
230
+ }));
231
+ const restoreResults = await controller.run();
232
+ const isError = (res) => Object.prototype.toString.call(res) === '[object Error]';
233
+ const firstRestoreError = restoreResults.find(isError);
234
+ if (firstRestoreError) {
235
+ throw new error_1.CloudBaseError(`从备份恢复远端文件失败:${firstRestoreError.message}`);
236
+ }
237
+ }
238
+ if (addedKeys.length > 0) {
239
+ await this.deleteFileCustom(addedKeys, bucket, region);
240
+ }
241
+ }
242
+ /**
243
+ * 删除备份前缀下的全部对象。
244
+ * 备份目录以 . 开头,内部使用空 Marker 列举,不受默认排除规则影响。
245
+ */
246
+ async cleanupBackup(options) {
247
+ const { bucket, region, backupPrefix } = options;
248
+ // 备份目录以 . 开头,会被 walkCloudDirCustom 默认 marker='/' 排除,
249
+ // 必须显式传 marker='' 才能列举到备份对象
250
+ await this.deleteDirectoryCustom({
251
+ cloudPath: backupPrefix,
252
+ bucket,
253
+ region,
254
+ marker: ''
255
+ });
256
+ }
257
+ /**
258
+ * 清理发布后远端残留的冗余文件。
259
+ * 仅删除 removedKeys 中位于 prefix 范围内的 Key,排除 .cloudbase-backup/;
260
+ * 单个删除失败记入 failedKeys,不影响其他文件。
261
+ */
262
+ async pruneRemote(options) {
263
+ const { bucket, region, prefix, removedKeys, parallel = 20 } = options;
264
+ const normalizedPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
265
+ const prefixWithSlash = normalizedPrefix ? `${normalizedPrefix}/` : '';
266
+ const backupPrefix = CONSISTENT_DEPLOY_BACKUP_PREFIX;
267
+ const keysToDelete = Array.from(new Set(removedKeys
268
+ .map(key => String(key || '').replace(/^\/+/, ''))
269
+ .filter(key => key &&
270
+ !key.startsWith(backupPrefix) &&
271
+ (!prefixWithSlash || key.startsWith(prefixWithSlash)) // 限制在 prefix 范围内
272
+ )));
273
+ if (!keysToDelete.length) {
274
+ return { deletedKeys: [], failedKeys: [] };
275
+ }
276
+ const cos = this.getCos();
277
+ const deleteObject = util_1.default.promisify(cos.deleteObject).bind(cos);
278
+ const controller = new parallel_1.AsyncTaskParallelController(parallel, 50);
279
+ controller.loadTasks(keysToDelete.map(key => async () => {
280
+ try {
281
+ await deleteObject({
282
+ Bucket: bucket,
283
+ Region: region,
284
+ Key: key
285
+ });
286
+ return { key };
287
+ }
288
+ catch (error) {
289
+ return {
290
+ key,
291
+ error: (error === null || error === void 0 ? void 0 : error.message) || String(error)
292
+ };
293
+ }
294
+ }));
295
+ const results = await controller.run();
296
+ const deletedKeys = [];
297
+ const failedKeys = [];
298
+ results.forEach(result => {
299
+ if (result && typeof result.error === 'string') {
300
+ failedKeys.push(result);
301
+ }
302
+ else if (result === null || result === void 0 ? void 0 : result.key) {
303
+ deletedKeys.push(result.key);
304
+ }
305
+ });
306
+ return { deletedKeys, failedKeys };
307
+ }
308
+ /**
309
+ * 一致性目录上传编排(safe、verify、prune)
310
+ *
311
+ * 执行流程(按顺序):
312
+ * 1. 扫描本地目录,生成 manifest(key/md5/size)
313
+ * 2. safe 或 prune 开启时:拉取远端快照 manifest,与本地做 diff
314
+ * (safe 用 diff 判定回滚范围;prune 用 diff.removed 判定待清理文件)
315
+ * 3. safe 开启时:将远端文件服务端复制到备份前缀 .cloudbase-backup/<时间戳>/
316
+ * 4. 执行目录上传(资源文件先传,入口文件 entries 最后传)
317
+ * 5. verify 开启时:重新拉取远端 manifest,逐文件校验 ETag/MD5 与 size
318
+ * 6. 上传与校验全部成功后,prune 开启时:删除 diff.removed 中仍冗余的远端文件
319
+ * 7. 返回 uploadedKeys / verified / backupPrefix / prune 结果
320
+ *
321
+ * 失败时的行为:
322
+ * - 步骤 4/5 失败且 safe 开启:自动从备份回滚(恢复被覆盖文件、删除本次新增
323
+ * 文件),回滚成功后抛出“已自动回滚”错误,备份保留供人工核查
324
+ * - 回滚失败:保留备份并在错误信息中给出备份位置,供人工恢复
325
+ * - 备份一律不由本编排自动清理,均需调用方确认后通过 cleanupBackup 显式删除
326
+ * - prune 仅在步骤 4/5 全部成功后执行,失败发布绝不会触发远端删除
327
+ *
328
+ * ⚠️ 危险参数:prune
329
+ * prune 会删除云端 cloudPath 前缀下、本次本地构建中不存在的全部文件。
330
+ * 若调用方误传了错误的 localPath/cloudPath,整目录都会被判定为“冗余”而删除。
331
+ * 发布成功后备份默认保留、不自动清理:prune 误删的文件可从备份前缀
332
+ * (.cloudbase-backup/<时间戳>/)人工恢复;调用方确认无误后应通过
333
+ * cleanupBackup 显式清理备份。调用方(CLI)仍须在开启 prune 前要求用户二次确认。
334
+ */
335
+ async uploadDirectoryConsistent(options) {
336
+ const { localPath, cloudPath = '', bucket, region, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500, onProgress, onFileFinish, verify = false, safe = false, prune = false, entries } = options;
337
+ const resolvePath = path_1.default.resolve(localPath);
338
+ const resolveLocalPath = path_1.default.join(resolvePath, path_1.default.sep);
339
+ const filePaths = await this.walkLocalDir(resolveLocalPath, ignore);
340
+ // 生成本地 manifest
341
+ const localManifest = await this.buildLocalManifest({
342
+ localPath,
343
+ cloudPath,
344
+ filePaths,
345
+ parallel
346
+ });
347
+ let backupPrefix;
348
+ let diff;
349
+ if (safe || prune) {
350
+ const remoteSnapshot = await this.getRemoteManifestCustom({
351
+ bucket,
352
+ region,
353
+ prefix: cloudPath
354
+ });
355
+ // 发布前快照同时用于 safe 回滚判定和 prune 待删文件判定。
356
+ diff = this.diffManifest(localManifest, remoteSnapshot);
357
+ }
358
+ if (safe) {
359
+ // COS 对象 Key 不保留前导斜杠,备份前缀使用 .cloudbase-backup/ 形式
360
+ backupPrefix = `${CONSISTENT_DEPLOY_BACKUP_PREFIX}${Date.now()}/`;
361
+ // 备份远端
362
+ await this.backupRemote({
363
+ bucket,
364
+ region,
365
+ prefix: cloudPath,
366
+ backupPrefix,
367
+ parallel
368
+ });
369
+ }
370
+ try {
371
+ await this.uploadDirectoryCustom({
372
+ localPath,
373
+ cloudPath,
374
+ entryFiles: entries,
375
+ bucket,
376
+ region,
377
+ ignore,
378
+ fileId,
379
+ parallel,
380
+ retryCount,
381
+ retryInterval,
382
+ onProgress,
383
+ onFileFinish
384
+ });
385
+ // 上传完成后校验
386
+ if (verify) {
387
+ const remoteManifest = await this.getRemoteManifestCustom({
388
+ bucket,
389
+ region,
390
+ prefix: cloudPath
391
+ });
392
+ const verifyResult = this.verifyManifest(localManifest, remoteManifest);
393
+ if (!verifyResult.ok) {
394
+ throw new error_1.CloudBaseError(`一致性校验失败:missing=${verifyResult.missing.join(',') || '[]'}; mismatched=${verifyResult.mismatched.join(',') || '[]'}`);
395
+ }
396
+ }
397
+ }
398
+ catch (error) {
399
+ if (!safe || !backupPrefix || !diff) {
400
+ throw error;
401
+ }
402
+ // 失败时回滚;备份保留供人工核查,由调用方确认后通过 cleanupBackup 显式清理
403
+ try {
404
+ await this.restoreFromBackup({
405
+ bucket,
406
+ region,
407
+ backupPrefix,
408
+ overwrittenKeys: diff.overwritten,
409
+ addedKeys: diff.added,
410
+ parallel
411
+ });
412
+ }
413
+ catch (rollbackError) {
414
+ throw new error_1.CloudBaseError(`一致性发布失败,且回滚失败,请使用备份人工恢复:${backupPrefix};原始错误:${error.message};回滚错误:${rollbackError.message}`);
415
+ }
416
+ throw new error_1.CloudBaseError(`一致性发布失败,已自动回滚,备份保留于 ${backupPrefix},确认无误后可调用 cleanupBackup 清理:${error.message}`);
417
+ }
418
+ // 上传与可选校验均成功后才允许清理远端冗余文件。
419
+ // 删除失败会记录在结果中,不触发已经成功发布内容的回滚。
420
+ const pruneResult = prune
421
+ ? await this.pruneRemote({
422
+ bucket,
423
+ region,
424
+ prefix: cloudPath,
425
+ removedKeys: (diff === null || diff === void 0 ? void 0 : diff.removed) || [],
426
+ parallel
427
+ })
428
+ : undefined;
429
+ // 发布成功后保留备份:prune 若误删文件,仍可从备份前缀人工恢复。
430
+ // 备份不由本编排自动清理,调用方确认无误后通过 cleanupBackup 显式删除。
431
+ return {
432
+ uploadedKeys: localManifest.map(item => item.key),
433
+ verified: verify,
434
+ backupPrefix,
435
+ prune: pruneResult
436
+ };
437
+ }
438
+ // ==============================================================
33
439
  /**
34
440
  * 上传文件
35
441
  * localPath 为文件夹时,会尝试在文件夹中寻找 cloudPath 中的文件名
@@ -178,7 +584,7 @@ class StorageService {
178
584
  * @returns {Promise<void>}
179
585
  */
180
586
  async uploadDirectoryCustom(options) {
181
- const { localPath, cloudPath, bucket, region, onProgress, onFileFinish, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
587
+ const { localPath, cloudPath, entryFiles, bucket, region, onProgress, onFileFinish, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
182
588
  // 此处不检查路径是否存在
183
589
  // 绝对路径 /var/blog/xxxx
184
590
  const resolvePath = path_1.default.resolve(localPath);
@@ -249,10 +655,26 @@ class StorageService {
249
655
  localPath: stats.filePath,
250
656
  cloudPath: stats.cloudFileKey
251
657
  }));
658
+ const { resourceFiles, entryUploadFiles } = (0, helper_1.splitEntryUploadFiles)(filesForUpload, entryFiles);
252
659
  // 调用 uploadFilesCustom(智能混合模式:并行→串行)
253
660
  // 不再直接调用 uploadFilesWithRetry(纯并行重试,容易 socket hang up)
661
+ // 第一阶段先上传资源文件;这里必须 await,确保入口文件不会与资源文件混传。
662
+ // 如果资源组上传失败,会直接中断,第二阶段不会开始。
663
+ await this.uploadFilesCustom({
664
+ files: resourceFiles,
665
+ bucket,
666
+ region,
667
+ ignore: [],
668
+ onProgress,
669
+ onFileFinish,
670
+ fileId,
671
+ parallel,
672
+ retryCount,
673
+ retryInterval
674
+ });
675
+ // 第二阶段再上传入口文件(默认各级 index.html),避免入口文件先于其依赖资源对外可见。
254
676
  return this.uploadFilesCustom({
255
- files: filesForUpload,
677
+ files: entryUploadFiles,
256
678
  bucket,
257
679
  region,
258
680
  ignore: [],
@@ -781,7 +1203,7 @@ class StorageService {
781
1203
  * @returns {Promise<void>}
782
1204
  */
783
1205
  async deleteDirectoryCustom(options) {
784
- const { cloudPath, bucket, region } = options;
1206
+ const { cloudPath, bucket, region, marker } = options;
785
1207
  const key = this.getCloudKey(cloudPath);
786
1208
  const cos = this.getCos();
787
1209
  const deleteMultipleObject = util_1.default.promisify(cos.deleteMultipleObject).bind(cos);
@@ -789,7 +1211,8 @@ class StorageService {
789
1211
  const files = await this.walkCloudDirCustom({
790
1212
  bucket,
791
1213
  region,
792
- prefix: key
1214
+ prefix: key,
1215
+ marker
793
1216
  });
794
1217
  // 文件为空时,不能调用删除接口
795
1218
  if (!files.length) {
@@ -2936,6 +3359,27 @@ class StorageService {
2936
3359
  }
2937
3360
  }
2938
3361
  exports.StorageService = StorageService;
3362
+ __decorate([
3363
+ (0, utils_1.preLazy)()
3364
+ ], StorageService.prototype, "getRemoteManifest", null);
3365
+ __decorate([
3366
+ (0, utils_1.preLazy)()
3367
+ ], StorageService.prototype, "getRemoteManifestCustom", null);
3368
+ __decorate([
3369
+ (0, utils_1.preLazy)()
3370
+ ], StorageService.prototype, "backupRemote", null);
3371
+ __decorate([
3372
+ (0, utils_1.preLazy)()
3373
+ ], StorageService.prototype, "restoreFromBackup", null);
3374
+ __decorate([
3375
+ (0, utils_1.preLazy)()
3376
+ ], StorageService.prototype, "cleanupBackup", null);
3377
+ __decorate([
3378
+ (0, utils_1.preLazy)()
3379
+ ], StorageService.prototype, "pruneRemote", null);
3380
+ __decorate([
3381
+ (0, utils_1.preLazy)()
3382
+ ], StorageService.prototype, "uploadDirectoryConsistent", null);
2939
3383
  __decorate([
2940
3384
  (0, utils_1.preLazy)()
2941
3385
  ], StorageService.prototype, "uploadFile", null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/manager-node",
3
- "version": "5.8.0-beta.2",
3
+ "version": "5.8.1",
4
4
  "description": "The node manage service api for cloudbase.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@cloudbase/signature-nodejs": "^1.0.0",
45
- "@cloudbase/toolbox": "^0.8.3-beta.2",
45
+ "@cloudbase/toolbox": "~0.8.3",
46
46
  "@cloudbase/database": "^0.6.2",
47
47
  "archiver": "^3.1.1",
48
48
  "cos-nodejs-sdk-v5": "^2.14.0",
@@ -1,6 +1,6 @@
1
1
  import { Environment } from '../environment';
2
2
  import { IResponseInfo, AuthDomain, EnvInfo, LoginConfigItem, ICheckTcbServiceRes, ICreatePostpayRes, EnvBillingInfoItem, PriceResult } from '../interfaces';
3
- import { CalculatePackageCreatePriceParams, CalculatePackageRenewPriceParams, CalculatePackageModifyPriceParams, DescribeHttpServiceRouteParams, DescribeHttpServiceRouteRes, CreateHttpServiceRouteParams, CreateHttpServiceRouteRes, ModifyHttpServiceRouteParams, ModifyHttpServiceRouteRes, DeleteHttpServiceRouteParams, DeleteHttpServiceRouteRes, BindCustomDomainParams, BindCustomDomainRes, DeleteCustomDomainParams, DeleteCustomDomainRes } from './type';
3
+ import { CalculatePackageCreatePriceParams, CalculatePackageRenewPriceParams, CalculatePackageModifyPriceParams, DescribeHttpServiceRouteParams, DescribeHttpServiceRouteRes, VerifyHttpServiceRouteParams, VerifyHttpServiceRouteRes, CreateHttpServiceRouteParams, CreateHttpServiceRouteRes, ModifyHttpServiceRouteParams, ModifyHttpServiceRouteRes, DeleteHttpServiceRouteParams, DeleteHttpServiceRouteRes, BindCustomDomainParams, BindCustomDomainRes, DeleteCustomDomainParams, DeleteCustomDomainRes, DescribeCertificatesParams, DescribeCertificatesRes } from './type';
4
4
  import { CreateBillingDealParams, CreateEnvParams, BaasPackageInfo, DescribeBaasPackageListParams, ModifyEnvPlanParams, RenewEnvParams, DestroyEnvParams, DescribeEnvsParams, DescribeEnvInfoParams, DescribeEnvInfoResponse, DescribeEnvAccountCircleParams, DescribeEnvAccountCircleRes, DescribeCreditsUsageDetailParams, EnvPkgCreditsUsage } from '../interfaces/tcb.interface';
5
5
  type SOURCE = 'miniapp' | 'qcloud';
6
6
  interface IDeleteDomainRes {
@@ -559,6 +559,7 @@ export declare class EnvService {
559
559
  private envType?;
560
560
  private commonService;
561
561
  private billingService;
562
+ private certificateCloudService;
562
563
  private tokenCache;
563
564
  constructor(environment: Environment);
564
565
  /**
@@ -882,12 +883,27 @@ export declare class EnvService {
882
883
  Usages: EnvPkgCreditsUsage[];
883
884
  RequestId: string;
884
885
  }>;
886
+ /**
887
+ * 获取当前凭证有权限访问的 SSL 证书列表。
888
+ *
889
+ * 证书用于自定义域名 HTTPS 配置,本接口不依赖 CloudBase 环境信息。
890
+ * @param {DescribeCertificatesParams} params 查询参数
891
+ * @returns {Promise<DescribeCertificatesRes>}
892
+ */
893
+ describeCertificates(params?: DescribeCertificatesParams): Promise<DescribeCertificatesRes>;
885
894
  /**
886
895
  * 查询HTTP访问服务域名路由信息
887
896
  * @param {DescribeHttpServiceRouteParams} params 查询参数
888
897
  * @returns {Promise<DescribeHttpServiceRouteRes>}
889
898
  */
890
899
  describeHttpServiceRoute(params: DescribeHttpServiceRouteParams): Promise<DescribeHttpServiceRouteRes>;
900
+ /**
901
+ * 校验HTTP访问服务域名路由
902
+ * 此操作为只读预检,不会创建任何资源。
903
+ * @param {VerifyHttpServiceRouteParams} params 校验参数
904
+ * @returns {Promise<VerifyHttpServiceRouteRes>}
905
+ */
906
+ verifyHttpServiceRoute(params: VerifyHttpServiceRouteParams): Promise<VerifyHttpServiceRouteRes>;
891
907
  /**
892
908
  * 创建HTTP访问服务域名路由
893
909
  * @param {CreateHttpServiceRouteParams} params 创建参数