@cloudbase/manager-node 4.2.0 → 4.2.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +0 -4
  2. package/lib/constant.js +1 -5
  3. package/lib/env/index.js +209 -10
  4. package/lib/function/index.js +5 -3
  5. package/lib/storage/index.js +3 -19
  6. package/lib/utils/cloud-api-request.js +0 -7
  7. package/lib/utils/http-request.js +3 -3
  8. package/package.json +3 -4
  9. package/src/access/index.ts +168 -0
  10. package/src/access/types.ts +55 -0
  11. package/src/billing/index.ts +43 -0
  12. package/src/cam/index.ts +106 -0
  13. package/src/cloudBaseRun/index.ts +40 -0
  14. package/src/cloudBaseRun/types.ts +24 -0
  15. package/src/common/index.ts +54 -0
  16. package/src/constant.ts +56 -0
  17. package/src/context.ts +18 -0
  18. package/src/database/index.ts +369 -0
  19. package/src/debug.ts +34 -0
  20. package/src/env/index.ts +614 -0
  21. package/src/environment.ts +156 -0
  22. package/src/environmentManager.ts +50 -0
  23. package/src/error.ts +27 -0
  24. package/src/function/index.ts +1362 -0
  25. package/src/function/packer.ts +164 -0
  26. package/src/function/types.ts +164 -0
  27. package/src/hosting/index.ts +698 -0
  28. package/src/index.ts +127 -0
  29. package/src/interfaces/base.interface.ts +8 -0
  30. package/src/interfaces/billing.interface.ts +21 -0
  31. package/src/interfaces/cam.interface.ts +28 -0
  32. package/src/interfaces/flexdb.interface.ts +104 -0
  33. package/src/interfaces/function.interface.ts +75 -0
  34. package/src/interfaces/index.ts +7 -0
  35. package/src/interfaces/storage.interface.ts +29 -0
  36. package/src/interfaces/tcb.interface.ts +636 -0
  37. package/src/storage/index.ts +1281 -0
  38. package/src/third/index.ts +24 -0
  39. package/src/user/index.ts +174 -0
  40. package/src/user/types.ts +21 -0
  41. package/src/utils/auth.ts +112 -0
  42. package/src/utils/cloud-api-request.ts +252 -0
  43. package/src/utils/cloudbase-request.ts +109 -0
  44. package/src/utils/envLazy.ts +15 -0
  45. package/src/utils/fs.ts +57 -0
  46. package/src/utils/http-request.ts +37 -0
  47. package/src/utils/index.ts +103 -0
  48. package/src/utils/parallel.ts +82 -0
  49. package/src/utils/uuid.ts +14 -0
  50. package/types/constant.d.ts +0 -7
  51. package/types/env/index.d.ts +17 -0
  52. package/types/function/index.d.ts +2 -1
  53. package/lib/utils/runenv.js +0 -8
  54. package/types/utils/runenv.d.ts +0 -1
@@ -0,0 +1,1362 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { FunctionPacker, CodeType } from './packer'
4
+ import { Environment } from '../environment'
5
+ import {
6
+ IResponseInfo,
7
+ ICloudFunction,
8
+ IFunctionLogOptions,
9
+ ICloudFunctionTrigger,
10
+ IFunctionInvokeRes,
11
+ IFunctionLogRes,
12
+ IFunctionDownloadUrlRes
13
+ } from '../interfaces'
14
+ import { CloudBaseError } from '../error'
15
+ import {
16
+ sleep,
17
+ preLazy,
18
+ delSync,
19
+ isDirectory,
20
+ CloudService,
21
+ compressToZip,
22
+ checkFullAccess
23
+ } from '../utils'
24
+ import { SCF_STATUS } from '../constant'
25
+ import { IFunctionInfo, IFunctionUpdateAttribute } from './types'
26
+
27
+ export interface IFunctionCode {
28
+ func: ICloudFunction // 云函数信息
29
+ functionRootPath?: string // 云函数根目录
30
+ base64Code?: string
31
+ functionPath?: string
32
+ }
33
+
34
+ export interface ICreateFunctionParam {
35
+ // 云函数信息
36
+ func: ICloudFunction & {
37
+ // 云接入路径
38
+ path?: string
39
+ }
40
+ functionRootPath?: string // 云函数根目录
41
+ force?: boolean // 是否覆盖同名云函数
42
+ base64Code?: string
43
+ functionPath?: string
44
+ codeSecret?: string // 代码保护密钥
45
+ }
46
+
47
+ export interface IUpdateFunctionCodeParam {
48
+ func: ICloudFunction // 云函数信息
49
+ functionPath?: string
50
+ functionRootPath?: string // 云函数的目录路径(可选) functionRootPath 与 base64Code 可任选其中一个
51
+ base64Code?: string // 云函数 ZIP 文件的 base64 编码(可选)
52
+ codeSecret?: string // 代码保护密钥
53
+ }
54
+
55
+ export interface IUpdateFunctionIncrementalCodeParam {
56
+ func: ICloudFunction
57
+ functionRootPath: string // 必选
58
+ deleteFiles?: Array<string> // 要删除的文件和目录列表
59
+ addFiles?: string // 新增或修改的文件路径 (指定单个文件或单个文件夹)
60
+ }
61
+
62
+ export interface ICreateFunctionRes {
63
+ triggerRes: IResponseInfo
64
+ configRes: IResponseInfo
65
+ codeRes: IResponseInfo
66
+ }
67
+
68
+ export interface IFunctionLayerOptions {
69
+ contentPath?: string
70
+ base64Content?: string
71
+ name: string
72
+ runtimes: string[] // 支持的运行时
73
+ description?: string // 层版本描述
74
+ licenseInfo?: string // 层的软件许可证
75
+ }
76
+
77
+ export interface ICreateLayerResponse extends IResponseInfo {
78
+ LayerVersion: number
79
+ }
80
+
81
+ export interface ILayerOptions {
82
+ name: string
83
+ version: number
84
+ }
85
+
86
+ export interface IVersionListOptions {
87
+ name: string
88
+ runtimes?: string[]
89
+ }
90
+
91
+ export interface ILayerListOptions {
92
+ offset?: number
93
+ limit?: number
94
+ runtime?: string
95
+ searchKey?: string
96
+ }
97
+
98
+ export interface ILayerVersionInfo {
99
+ CompatibleRuntimes: string[] // 版本适用的运行时
100
+ AddTime: string // 创建时间
101
+ Description: string // 版本描述
102
+ LicenseInfo: string // 许可证信息
103
+ LayerVersion: number // 版本号
104
+ LayerName: string // 层名称
105
+ Status: string // 层的具体版本当前状态
106
+ }
107
+
108
+ export interface IListLayerVersionsRes extends IResponseInfo {
109
+ LayerVersions: Array<ILayerVersionInfo>
110
+ }
111
+
112
+ export interface IListLayerRes extends IResponseInfo {
113
+ Layers: Array<ILayerVersionInfo>
114
+ TotalCount: number
115
+ }
116
+
117
+ export interface IGetLayerVersionRes extends IResponseInfo {
118
+ CompatibleRuntimes: string[] // 适配的运行时
119
+ CodeSha256: string // 层中版本文件的SHA256编码
120
+ Location: string // 层中版本文件的下载地址
121
+ AddTime: string // 版本的创建时间
122
+ Description: string // 版本的描述
123
+ LicenseInfo: string // 许可证信息
124
+ LayerVersion: number // 版本号
125
+ LayerName: string // 层名称
126
+ Status: string // 层的具体版本当前状态
127
+ }
128
+
129
+ export interface ISetProvisionedConcurrencyConfig {
130
+ functionName: string
131
+ qualifier: string
132
+ versionProvisionedConcurrencyNum: number
133
+ }
134
+
135
+ export interface IGetProvisionedConcurrencyConfig {
136
+ functionName: string
137
+ qualifier?: string
138
+ }
139
+
140
+ export interface IVersionProvisionedConcurrencyInfo {
141
+ AllocatedProvisionedConcurrencyNum: number // 设置的预置并发数。
142
+ AvailableProvisionedConcurrencyNum: number // 当前已完成预置的并发数。
143
+ Status: string // 预置任务状态,Done表示已完成,InProgress表示进行中,Failed表示部分或全部失败。
144
+ StatusReason: string // 对预置任务状态Status的说明。
145
+ Qualifier: string // 版本号
146
+ }
147
+ export interface IGetProvisionedConcurrencyRes extends IResponseInfo {
148
+ UnallocatedConcurrencyNum: number
149
+ Allocated: IVersionProvisionedConcurrencyInfo[]
150
+ }
151
+
152
+ export interface IPublishVersionParams {
153
+ functionName: string
154
+ description?: string
155
+ }
156
+
157
+ export interface IPublishVersionRes extends IResponseInfo {
158
+ FunctionVersion: string
159
+ CodeSize: number
160
+ MemorySize: number
161
+ Description: string
162
+ Handler: string
163
+ Timeout: number
164
+ Runtime: string
165
+ Namespace: string
166
+ }
167
+
168
+
169
+ export interface IListFunctionVersionParams {
170
+ functionName: string
171
+ offset?: number
172
+ limit?: number
173
+ order?: string
174
+ orderBy?: string
175
+ }
176
+
177
+ export interface IFunctionVersion {
178
+ Version: string
179
+ Description: string
180
+ AddTime: string
181
+ ModTime: string
182
+ Status: string //
183
+ }
184
+
185
+ export interface IFunctionVersionsRes extends IResponseInfo {
186
+ FunctionVersion: string[]
187
+ Versions: IFunctionVersion[]
188
+ TotalCount: number
189
+ }
190
+
191
+ export interface IVersionMatch {
192
+ Version: string // 函数版本名称
193
+ Key: string // 匹配规则的key,调用时通过传key来匹配规则路由到指定版本 header方式:key填写"invoke.headers.User",并在 invoke 调用函数时传参 RoutingKey:{"User":"value"}规则匹配调用
194
+ Method: string // 匹配方式。取值范围:range:范围匹配 exact:字符串精确匹配
195
+ Expression: string //
196
+ }
197
+
198
+ export interface IVersionWeight {
199
+ Version: string
200
+ Weight: number
201
+ }
202
+
203
+ export interface IRoutingConfig {
204
+ AdditionalVersionWeights?: IVersionWeight[]
205
+ AddtionVersionMatchs?: IVersionMatch[]
206
+ }
207
+
208
+ export interface IUpdateFunctionAliasConfig {
209
+ functionName: string // 函数名
210
+ name: string // 函数别名 如$DEFAULT
211
+ functionVersion: string // 函数版本
212
+ description?: string // 别名描述
213
+ routingConfig?: IRoutingConfig
214
+ }
215
+
216
+ export interface IGetFunctionAlias {
217
+ functionName: string // 函数名称
218
+ name: string // 别名
219
+ }
220
+
221
+ export interface IGetFunctionAliasRes extends IResponseInfo {
222
+ FunctionVersion: string
223
+ Name: string
224
+ RoutingConfig: IRoutingConfig
225
+ Description: string
226
+ AddTime: string
227
+ ModTime: string
228
+ }
229
+
230
+ // 是否为 Node 函数
231
+ function isNodeFunction(runtime: string) {
232
+ // 不严格限制
233
+ return runtime === 'Nodejs10.15' || runtime === 'Nodejs8.9' || runtime?.includes('Nodejs')
234
+ }
235
+
236
+ // 解析函数配置,换成请求参数
237
+ function configToParams(options: { func: ICloudFunction; codeSecret: string; baseParams: any }) {
238
+ const { func, codeSecret, baseParams } = options
239
+ let installDependency
240
+ // Node 函数默认安装依赖
241
+ installDependency = isNodeFunction(func.runtime) ? 'TRUE' : 'FALSE'
242
+ // 是否安装依赖,选项可以覆盖
243
+ if (typeof func.installDependency !== 'undefined') {
244
+ installDependency = func.installDependency ? 'TRUE' : 'FALSE'
245
+ }
246
+
247
+ // 转换环境变量
248
+ const envVariables = Object.keys(func.envVariables || {}).map(key => ({
249
+ Key: key,
250
+ Value: func.envVariables[key]
251
+ }))
252
+
253
+ // 当不存在 L5 配置时,不修改 L5 状态,否则根据 true/false 进行修改
254
+ const l5Enable = typeof func?.l5 === 'undefined' ? null : func?.l5 ? 'TRUE' : 'FALSE'
255
+
256
+ const params: any = {
257
+ ...baseParams,
258
+ FunctionName: func.name,
259
+ // 不可选择
260
+ L5Enable: l5Enable
261
+ }
262
+
263
+ // 函数绑定的角色
264
+ params.Role = func.role || params.Role
265
+ // 修复参数存在 undefined 字段时,会出现鉴权失败的情况
266
+ // Environment 为覆盖式修改,不保留已有字段
267
+ envVariables.length && (params.Environment = { Variables: envVariables })
268
+ // 处理入口
269
+ params.Handler = func.handler || 'index.main'
270
+ // 默认超时时间为 10S
271
+ params.Timeout = Number(func.timeout) || 10
272
+ // 默认运行环境 Nodejs8.9
273
+ params.Runtime = func.runtime || 'Nodejs8.9'
274
+
275
+ if (func?.vpc?.subnetId !== undefined && func?.vpc?.vpcId !== undefined) {
276
+ // VPC 网络
277
+ params.VpcConfig = {
278
+ SubnetId: func?.vpc?.subnetId,
279
+ VpcId: func?.vpc?.vpcId
280
+ }
281
+ }
282
+ // 运行内存
283
+ params.MemorySize = func.memorySize || 256
284
+ // 自动安装依赖
285
+ params.InstallDependency = installDependency
286
+
287
+ // 代码保护
288
+ if (codeSecret || func.codeSecret) {
289
+ params.CodeSecret = codeSecret || func.codeSecret
290
+ }
291
+
292
+ // 函数层
293
+ if (func?.layers?.length) {
294
+ const transformLayers = func.layers.map(item => ({
295
+ LayerName: item.name,
296
+ LayerVersion: item.version
297
+ }))
298
+ params.Layers = transformLayers
299
+ }
300
+
301
+ return params
302
+ }
303
+
304
+ export class FunctionService {
305
+ private environment: Environment
306
+ private vpcService: CloudService
307
+ private scfService: CloudService
308
+
309
+ constructor(environment: Environment) {
310
+ this.environment = environment
311
+ this.scfService = new CloudService(environment.cloudBaseContext, 'scf', '2018-04-16')
312
+ this.vpcService = new CloudService(environment.cloudBaseContext, 'vpc', '2017-03-12')
313
+ }
314
+
315
+ /**
316
+ * 增量更新函数代码
317
+ * @param {IUpdateFunctionIncrementalCodeParam} funcParam
318
+ * @returns {Promise<void>}
319
+ * @memberof FunctionService
320
+ */
321
+ @preLazy()
322
+ public async updateFunctionIncrementalCode(
323
+ funcParam: IUpdateFunctionIncrementalCodeParam
324
+ ): Promise<IResponseInfo> {
325
+ const { namespace } = this.getFunctionConfig()
326
+ const { functionRootPath, func, deleteFiles, addFiles } = funcParam
327
+ const { name, runtime } = func
328
+ const params: any = {
329
+ FunctionName: name,
330
+ Namespace: namespace
331
+ }
332
+
333
+ let packer: FunctionPacker
334
+ let base64
335
+
336
+ if (deleteFiles) {
337
+ params.DeleteFiles = deleteFiles
338
+ }
339
+
340
+ if (addFiles) {
341
+ // 将选中的增量文件或增量文件夹 转base64
342
+ const codeType: CodeType = runtime === 'Java8' ? CodeType.JavaFile : CodeType.File
343
+ packer = new FunctionPacker({
344
+ codeType,
345
+ name,
346
+ root: functionRootPath,
347
+ ignore: [],
348
+ incrementalPath: addFiles
349
+ })
350
+ await packer.build()
351
+ base64 = await packer.getBase64Code()
352
+ if (!base64) {
353
+ throw new CloudBaseError('函数不存在!')
354
+ }
355
+ params.AddFiles = base64
356
+ }
357
+
358
+ return this.scfService.request<IResponseInfo>('UpdateFunctionIncrementalCode', params)
359
+ }
360
+
361
+ /**
362
+ * 创建云函数
363
+ * @param {ICreateFunctionParam} funcParam
364
+ * @returns {(Promise<IResponseInfo | ICreateFunctionRes>)}
365
+ */
366
+ @preLazy()
367
+ public async createFunction(
368
+ funcParam: ICreateFunctionParam
369
+ ): Promise<IResponseInfo | ICreateFunctionRes> {
370
+ const { namespace } = this.getFunctionConfig()
371
+ const {
372
+ func,
373
+ functionRootPath,
374
+ force = false,
375
+ base64Code,
376
+ codeSecret,
377
+ functionPath
378
+ } = funcParam
379
+ const funcName = func.name
380
+
381
+ const params: IFunctionUpdateAttribute = configToParams({
382
+ func,
383
+ codeSecret,
384
+ baseParams: {
385
+ Namespace: namespace,
386
+ Role: 'TCB_QcsRole',
387
+ Stamp: 'MINI_QCBASE'
388
+ }
389
+ })
390
+
391
+ params.Code = await this.getCodeParams(
392
+ {
393
+ func,
394
+ base64Code,
395
+ functionPath,
396
+ functionRootPath
397
+ },
398
+ params.InstallDependency
399
+ )
400
+
401
+ try {
402
+ // 创建云函数
403
+ const res = await this.scfService.request<IResponseInfo | ICreateFunctionRes>(
404
+ 'CreateFunction',
405
+ params
406
+ )
407
+ // 等待函数状态正常
408
+ await this.waitFunctionActive(funcName, codeSecret)
409
+ // 创建函数触发器、失败自动重试
410
+ await this.retryCreateTrigger(funcName, func.triggers)
411
+
412
+ // 设置路径,创建云接入路径
413
+ if (func.path) {
414
+ await this.createAccessPath(funcName, func.path)
415
+ }
416
+
417
+ // 检查函数状态
418
+ await this.waitFunctionActive(funcName, codeSecret)
419
+
420
+ return res
421
+ } catch (e) {
422
+ // 函数存在
423
+ const functionExist =
424
+ e.code === 'ResourceInUse.FunctionName' || e.code === 'ResourceInUse.Function'
425
+ // 已存在同名函数,强制更新
426
+ if (functionExist && force) {
427
+ // 1. 更新函数配置和代码,同名函数可能存在 codeSecret,先修改代码,清除 codeSecret
428
+ const codeRes = await this.updateFunctionCode({
429
+ func,
430
+ base64Code,
431
+ functionPath,
432
+ functionRootPath,
433
+ codeSecret: codeSecret
434
+ })
435
+ // 等待函数状态正常
436
+ await this.waitFunctionActive(funcName, codeSecret)
437
+ // 2. 更新函数配置
438
+ const configRes = await this.updateFunctionConfig(func)
439
+ // 等待函数状态正常
440
+ await this.waitFunctionActive(funcName, codeSecret)
441
+ // 3. 创建函数触发器
442
+ const triggerRes = await this.retryCreateTrigger(funcName, func.triggers)
443
+
444
+ // 设置路径,创建云接入路径
445
+ if (func.path) {
446
+ await this.createAccessPath(funcName, func.path)
447
+ }
448
+
449
+ // 检查函数状态
450
+ await this.waitFunctionActive(funcName, codeSecret)
451
+ // 返回全部操作的响应值
452
+ return {
453
+ triggerRes,
454
+ configRes,
455
+ codeRes
456
+ }
457
+ }
458
+
459
+ // 不强制覆盖,抛出错误
460
+ if (e.message && !force) {
461
+ throw new CloudBaseError(`[${funcName}] 部署失败:\n${e.message}`, {
462
+ code: e.code,
463
+ requestId: e.requestId
464
+ })
465
+ }
466
+
467
+ throw e
468
+ }
469
+ }
470
+
471
+ /**
472
+ * @param {number} [limit=20]
473
+ * @param {number} [offset=0]
474
+ * @returns {Promise<{
475
+ * Functions: Record<string, string>[]
476
+ * RequestId: string
477
+ * TotalCount: number
478
+ * }>}
479
+ * @memberof FunctionService
480
+ */
481
+ @preLazy()
482
+ async getFunctionList(
483
+ limit = 20,
484
+ offset = 0
485
+ ): Promise<{
486
+ Functions: Record<string, string>[]
487
+ RequestId: string
488
+ TotalCount: number
489
+ }> {
490
+ // 获取Function 环境配置
491
+ const { namespace } = this.getFunctionConfig()
492
+
493
+ const res: any = await this.scfService.request('ListFunctions', {
494
+ Namespace: namespace,
495
+ Limit: limit,
496
+ Offset: offset
497
+ })
498
+ return res
499
+ }
500
+
501
+ /**
502
+ * 列出函数
503
+ * @param {number} [limit=20]
504
+ * @param {number} [offset=0]
505
+ * @returns {Promise<Record<string, string>[]>}
506
+ */
507
+ @preLazy()
508
+ async listFunctions(limit = 20, offset = 0): Promise<Record<string, string>[]> {
509
+ // 获取Function 环境配置
510
+ const { namespace } = this.getFunctionConfig()
511
+
512
+ const res: any = await this.scfService.request('ListFunctions', {
513
+ Namespace: namespace,
514
+ Limit: limit,
515
+ Offset: offset
516
+ })
517
+ const { Functions = [] } = res
518
+ const data: Record<string, string>[] = []
519
+ Functions.forEach(func => {
520
+ const { FunctionId, FunctionName, Runtime, AddTime, ModTime, Status } = func
521
+ data.push({
522
+ FunctionId,
523
+ FunctionName,
524
+ Runtime,
525
+ AddTime,
526
+ ModTime,
527
+ Status
528
+ })
529
+ })
530
+
531
+ return data
532
+ }
533
+
534
+ /**
535
+ * 删除云函数
536
+ * @param {string} name 云函数名称
537
+ * @param {string} qualifier 需要删除的版本号,不填默认删除函数下全部版本。
538
+ * @returns {Promise<IResponseInfo>}
539
+ */
540
+ @preLazy()
541
+ async deleteFunction(name: string, qualifier?: string): Promise<IResponseInfo> {
542
+ const { namespace } = this.getFunctionConfig()
543
+ return this.scfService.request('DeleteFunction', {
544
+ FunctionName: name,
545
+ Namespace: namespace,
546
+ Qualifier: qualifier
547
+ })
548
+ }
549
+
550
+ /**
551
+ * 获取云函数详细信息
552
+ * @param {string} name 云函数名称
553
+ * @returns {Promise<Record<string, string>>}
554
+ */
555
+ @preLazy()
556
+ async getFunctionDetail(name: string, codeSecret?: string): Promise<IFunctionInfo> {
557
+ const { namespace } = this.getFunctionConfig()
558
+
559
+ const params: any = {
560
+ FunctionName: name,
561
+ Namespace: namespace,
562
+ ShowCode: 'TRUE'
563
+ }
564
+
565
+ if (codeSecret) {
566
+ params.CodeSecret = codeSecret
567
+ }
568
+
569
+ const data = await this.scfService.request<IFunctionInfo>('GetFunction', params)
570
+
571
+ // 解析 VPC 配置
572
+ const { VpcId = '', SubnetId = '' } = data.VpcConfig || {}
573
+ if (VpcId && SubnetId) {
574
+ try {
575
+ const vpcs = await this.getVpcs()
576
+ const subnets = await this.getSubnets(VpcId)
577
+ const vpc = vpcs.find(item => item.VpcId === VpcId)
578
+ const subnet = subnets.find(item => item.SubnetId === SubnetId)
579
+ data.VpcConfig = {
580
+ vpc,
581
+ subnet
582
+ }
583
+ } catch (e) {
584
+ data.VpcConfig = {
585
+ vpc: '',
586
+ subnet: ''
587
+ }
588
+ }
589
+ }
590
+
591
+ return data
592
+ }
593
+
594
+ /**
595
+ * 获取函数日志
596
+ * @param {{
597
+ * name: string
598
+ * offset: number
599
+ * limit: number
600
+ * order: string
601
+ * orderBy: string
602
+ * startTime: string
603
+ * endTime: string
604
+ * requestId: string
605
+ * }} options
606
+ * @returns {Promise<IFunctionLogRes>}
607
+ */
608
+ @preLazy()
609
+ async getFunctionLogs(options: IFunctionLogOptions): Promise<IFunctionLogRes> {
610
+ const {
611
+ name,
612
+ offset = 0,
613
+ limit = 10,
614
+ order,
615
+ orderBy,
616
+ startTime,
617
+ endTime,
618
+ requestId
619
+ } = options
620
+ const { namespace } = this.getFunctionConfig()
621
+
622
+ const params = {
623
+ Namespace: namespace,
624
+ FunctionName: name,
625
+ Offset: offset,
626
+ Limit: limit,
627
+ Order: order,
628
+ OrderBy: orderBy,
629
+ StartTime: startTime,
630
+ EndTime: endTime,
631
+ FunctionRequestId: requestId
632
+ }
633
+
634
+ const res: IFunctionLogRes = await this.scfService.request('GetFunctionLogs', params)
635
+ return res
636
+ }
637
+
638
+ /**
639
+ * 更新云函数配置
640
+ * @param {ICloudFunction} func 云函数配置
641
+ * @returns {Promise<IResponseInfo>}
642
+ */
643
+ @preLazy()
644
+ async updateFunctionConfig(func: ICloudFunction): Promise<IResponseInfo> {
645
+ const { namespace } = this.getFunctionConfig()
646
+
647
+ const envVariables = Object.keys(func.envVariables || {}).map(key => ({
648
+ Key: key,
649
+ Value: func.envVariables[key]
650
+ }))
651
+
652
+ // 当不存在 L5 配置时,不修改 L5 状态,否则根据 true/false 进行修改
653
+ const l5Enable = typeof func.l5 === 'undefined' ? null : func.l5 ? 'TRUE' : 'FALSE'
654
+
655
+ const params: any = {
656
+ FunctionName: func.name,
657
+ Namespace: namespace,
658
+ L5Enable: l5Enable
659
+ }
660
+
661
+ // 修复参数存在 undefined 字段时,会出现鉴权失败的情况
662
+ // Environment 为覆盖式修改,不保留已有字段
663
+ envVariables.length && (params.Environment = { Variables: envVariables })
664
+ // 不设默认超时时间,防止覆盖已有配置
665
+ func.timeout && (params.Timeout = func.timeout)
666
+ // 运行时
667
+ func.runtime && (params.Runtime = func.runtime)
668
+
669
+ if (func?.vpc?.subnetId !== undefined && func?.vpc?.vpcId !== undefined) {
670
+ // VPC 网络
671
+ params.VpcConfig = {
672
+ SubnetId: func?.vpc?.subnetId,
673
+ VpcId: func?.vpc?.vpcId
674
+ }
675
+ }
676
+
677
+ // 内存
678
+ func.memorySize && (params.MemorySize = func.memorySize)
679
+
680
+ // Node 函数默认安装依赖
681
+ isNodeFunction(func.runtime) && (params.InstallDependency = 'TRUE')
682
+ // 是否安装依赖,选项可以覆盖
683
+ if (typeof func.installDependency !== 'undefined') {
684
+ params.InstallDependency = func.installDependency ? 'TRUE' : 'FALSE'
685
+ }
686
+
687
+ // 函数层
688
+ if (func?.layers?.length) {
689
+ const transformLayers = func.layers.map(item => ({
690
+ LayerName: item.name,
691
+ LayerVersion: item.version
692
+ }))
693
+ params.Layers = transformLayers
694
+ }
695
+
696
+ return this.scfService.request('UpdateFunctionConfiguration', params)
697
+ }
698
+
699
+ /**
700
+ *
701
+ * @param {IUpdateFunctionCodeParam} funcParam
702
+ * @returns {Promise<IResponseInfo>}
703
+ * @memberof FunctionService
704
+ */
705
+ @preLazy()
706
+ async updateFunctionCode(funcParam: IUpdateFunctionCodeParam): Promise<IResponseInfo> {
707
+ const { func, functionRootPath, base64Code, codeSecret, functionPath } = funcParam
708
+ const funcName = func.name
709
+
710
+ const { namespace } = this.getFunctionConfig()
711
+
712
+ let installDependency
713
+ // Node 函数默认安装依赖
714
+ installDependency = isNodeFunction(func.runtime) ? 'TRUE' : 'FALSE'
715
+ // 是否安装依赖,选项可以覆盖
716
+ if (typeof func.installDependency !== 'undefined') {
717
+ installDependency = func.installDependency ? 'TRUE' : 'FALSE'
718
+ }
719
+
720
+ const codeParams = await this.getCodeParams(
721
+ {
722
+ func,
723
+ functionPath,
724
+ functionRootPath,
725
+ base64Code
726
+ },
727
+ installDependency
728
+ )
729
+
730
+ const params: any = {
731
+ FunctionName: funcName,
732
+ Namespace: namespace,
733
+ Handler: func.handler || 'index.main',
734
+ InstallDependency: installDependency,
735
+ ...codeParams
736
+ }
737
+
738
+ if (codeSecret) {
739
+ params.CodeSecret = codeSecret
740
+ }
741
+
742
+ try {
743
+ // 等待函数状态正常
744
+ await this.waitFunctionActive(funcName, codeSecret)
745
+ // 更新云函数代码
746
+ const res = await this.scfService.request<IResponseInfo>('UpdateFunctionCode', params)
747
+ if (installDependency && func.isWaitInstall === true) {
748
+ await this.waitFunctionActive(funcName, codeSecret)
749
+ }
750
+ return res
751
+ } catch (e) {
752
+ throw new CloudBaseError(`[${funcName}] 函数代码更新失败: ${e.message}`, {
753
+ code: e.code
754
+ })
755
+ }
756
+ }
757
+
758
+ /**
759
+ * 调用云函数
760
+ * @param {string} name 云函数名称
761
+ * @param {Record<string, any>} params 调用函数传入参数
762
+ * @returns {Promise<IFunctionInvokeRes>}
763
+ */
764
+ @preLazy()
765
+ async invokeFunction(name: string, params?: Record<string, any>): Promise<IFunctionInvokeRes> {
766
+ const { namespace } = this.getFunctionConfig()
767
+
768
+ const _params: any = {
769
+ FunctionName: name,
770
+ Namespace: namespace,
771
+ LogType: 'Tail'
772
+ }
773
+
774
+ if (params) {
775
+ _params.ClientContext = JSON.stringify(params)
776
+ }
777
+
778
+ try {
779
+ const { RequestId, Result } = await this.scfService.request('Invoke', _params)
780
+ return {
781
+ RequestId,
782
+ ...Result
783
+ }
784
+ } catch (e) {
785
+ throw new CloudBaseError(`[${name}] 调用失败:\n${e.message}`)
786
+ }
787
+ }
788
+
789
+ /**
790
+ * 复制云函数
791
+ * @param {string} name 云函数名称
792
+ * @param {string} newFunctionName 新的云函数名称
793
+ * @param {string} targetEnvId 目标环境 Id
794
+ * @param {boolean} [force=false] 是否覆盖同名云函数
795
+ * @returns {Promise<IResponseInfo>}
796
+ */
797
+ /* eslint-disable-next-line */
798
+ @preLazy()
799
+ async copyFunction(
800
+ name: string,
801
+ newFunctionName: string,
802
+ targetEnvId?: string,
803
+ force = false
804
+ ): Promise<IResponseInfo> {
805
+ const { namespace } = this.getFunctionConfig()
806
+
807
+ if (!namespace || !name || !newFunctionName) {
808
+ throw new CloudBaseError('参数缺失')
809
+ }
810
+
811
+ return this.scfService.request('CopyFunction', {
812
+ FunctionName: name,
813
+ NewFunctionName: newFunctionName,
814
+ Namespace: namespace,
815
+ TargetNamespace: targetEnvId || namespace,
816
+ Override: force ? true : false
817
+ })
818
+ }
819
+
820
+ /**
821
+ * 创建云函数触发器
822
+ * @param {string} name 云函数名称
823
+ * @param {ICloudFunctionTrigger[]} triggers 云函数触发器配置
824
+ * @returns {Promise<IResponseInfo>}
825
+ */
826
+ @preLazy()
827
+ async createFunctionTriggers(
828
+ name: string,
829
+ triggers: ICloudFunctionTrigger[] = []
830
+ ): Promise<IResponseInfo> {
831
+ if (!triggers || !triggers.length) return null
832
+ const { namespace } = this.getFunctionConfig()
833
+
834
+ const parsedTriggers = triggers.map(item => {
835
+ if (item.type !== 'timer') {
836
+ throw new CloudBaseError(
837
+ `不支持的触发器类型 [${item.type}],目前仅支持定时触发器(timer)!`
838
+ )
839
+ }
840
+ return {
841
+ TriggerName: item.name,
842
+ Type: item.type,
843
+ TriggerDesc: item.config
844
+ }
845
+ })
846
+
847
+ return this.scfService.request('BatchCreateTrigger', {
848
+ FunctionName: name,
849
+ Namespace: namespace,
850
+ Triggers: JSON.stringify(parsedTriggers),
851
+ Count: parsedTriggers.length
852
+ })
853
+ }
854
+
855
+ /**
856
+ * 删除云函数触发器
857
+ * @param {string} name 云函数名称
858
+ * @param {string} triggerName 云函数触发器名称
859
+ * @returns {Promise<IResponseInfo>}
860
+ */
861
+ @preLazy()
862
+ async deleteFunctionTrigger(name: string, triggerName: string): Promise<IResponseInfo> {
863
+ const { namespace } = this.getFunctionConfig()
864
+
865
+ return this.scfService.request('DeleteTrigger', {
866
+ FunctionName: name,
867
+ Namespace: namespace,
868
+ TriggerName: triggerName,
869
+ Type: 'timer'
870
+ })
871
+ }
872
+
873
+ /**
874
+ * 获取云函数代码下载 链接
875
+ * @param {string} functionName
876
+ * @param {string} [codeSecret]
877
+ * @returns {Promise<IFunctionDownloadUrlRes>}
878
+ * @memberof FunctionService
879
+ */
880
+ @preLazy()
881
+ public async getFunctionDownloadUrl(
882
+ functionName: string,
883
+ codeSecret?: string
884
+ ): Promise<IFunctionDownloadUrlRes> {
885
+ const { namespace } = this.getFunctionConfig()
886
+
887
+ const params: any = {
888
+ FunctionName: functionName,
889
+ Namespace: namespace
890
+ }
891
+
892
+ if (codeSecret) {
893
+ params.CodeSecret = codeSecret
894
+ }
895
+
896
+ try {
897
+ const { Url, CodeSha256, RequestId } = await this.scfService.request(
898
+ 'GetFunctionAddress',
899
+ params
900
+ )
901
+ return { Url, RequestId, CodeSha256 }
902
+ } catch (e) {
903
+ throw new CloudBaseError(`[${functionName}] 获取函数代码下载链接失败:\n${e.message}`)
904
+ }
905
+ }
906
+
907
+ // 创建文件层版本
908
+ @preLazy()
909
+ public async createLayer(options: IFunctionLayerOptions): Promise<ICreateLayerResponse> {
910
+ const { env } = this.getFunctionConfig()
911
+ const {
912
+ contentPath = '',
913
+ name,
914
+ base64Content = '',
915
+ runtimes = [],
916
+ description = '',
917
+ licenseInfo = ''
918
+ } = options
919
+
920
+ let base64
921
+
922
+ if (base64Content) {
923
+ base64 = base64Content
924
+ } else if (isDirectory(contentPath)) {
925
+ // 压缩文件夹
926
+ const dirName = path.parse(contentPath).name
927
+ const dest = path.join(process.cwd(), `temp-${dirName}.zip`)
928
+ // ZIP 文件存在,删除 ZIP 文件
929
+ if (checkFullAccess(dest)) {
930
+ delSync(dest)
931
+ }
932
+ await compressToZip({
933
+ dirPath: contentPath,
934
+ outputPath: dest
935
+ })
936
+ // 转换成 base64
937
+ const fileBuffer = await fs.promises.readFile(dest)
938
+ base64 = fileBuffer.toString('base64')
939
+ delSync(dest)
940
+ } else {
941
+ const fileType = path.extname(contentPath)
942
+ if (fileType !== '.zip') {
943
+ throw new CloudBaseError('文件类型不正确,目前只支持 ZIP 文件!')
944
+ }
945
+ const fileBuffer = await fs.promises.readFile(contentPath)
946
+ base64 = fileBuffer.toString('base64')
947
+ }
948
+
949
+ return this.scfService.request('PublishLayerVersion', {
950
+ LayerName: name,
951
+ CompatibleRuntimes: runtimes,
952
+ Content: {
953
+ // 最大支持 20M
954
+ ZipFile: base64
955
+ },
956
+ Description: description,
957
+ LicenseInfo: licenseInfo,
958
+ Src: `TCB_${env}`
959
+ })
960
+ }
961
+
962
+ // 删除文件层版本
963
+ @preLazy()
964
+ public async deleteLayerVersion(options: ILayerOptions): Promise<IResponseInfo> {
965
+ const { name, version } = options
966
+
967
+ return this.scfService.request('DeleteLayerVersion', {
968
+ LayerName: name,
969
+ LayerVersion: version
970
+ })
971
+ }
972
+
973
+ // 获取层版本列表
974
+ @preLazy()
975
+ public async listLayerVersions(options: IVersionListOptions): Promise<IListLayerVersionsRes> {
976
+ const { name, runtimes } = options
977
+ let param: any = {
978
+ LayerName: name
979
+ }
980
+ if (runtimes?.length) {
981
+ param.CompatibleRuntime = runtimes
982
+ }
983
+ return this.scfService.request('ListLayerVersions', param)
984
+ }
985
+
986
+ // 获取文件层列表
987
+ @preLazy()
988
+ public async listLayers(options: ILayerListOptions): Promise<IListLayerRes> {
989
+ const { env } = this.getFunctionConfig()
990
+ const { limit = 20, offset = 0, runtime, searchKey } = options
991
+ let param: any = {
992
+ Limit: limit,
993
+ Offset: offset,
994
+ SearchKey: searchKey,
995
+ SearchSrc: `TCB_${env}`
996
+ }
997
+ if (runtime) {
998
+ param.CompatibleRuntime = runtime
999
+ }
1000
+
1001
+ return this.scfService.request('ListLayers', param)
1002
+ }
1003
+
1004
+ // 获取层版本详细信息
1005
+ @preLazy()
1006
+ public async getLayerVersion(options: ILayerOptions): Promise<IGetLayerVersionRes> {
1007
+ const { name, version } = options
1008
+
1009
+ return this.scfService.request('GetLayerVersion', {
1010
+ LayerName: name,
1011
+ LayerVersion: version
1012
+ })
1013
+ }
1014
+
1015
+ /**
1016
+ * 设置预置并发
1017
+ * @private
1018
+ * @param {IProvisionedConcurrencyConfig} concurrencyConfig
1019
+ * @returns
1020
+ * @memberof FunctionService
1021
+ */
1022
+ @preLazy()
1023
+ public async setProvisionedConcurrencyConfig(
1024
+ concurrencyConfig: ISetProvisionedConcurrencyConfig
1025
+ ): Promise<IResponseInfo> {
1026
+ const { namespace } = this.getFunctionConfig()
1027
+ const {
1028
+ functionName: FunctionName,
1029
+ qualifier: Qualifier,
1030
+ versionProvisionedConcurrencyNum: VersionProvisionedConcurrencyNum
1031
+ } = concurrencyConfig
1032
+
1033
+ return this.scfService.request('PutProvisionedConcurrencyConfig', {
1034
+ FunctionName,
1035
+ Qualifier,
1036
+ VersionProvisionedConcurrencyNum,
1037
+ Namespace: namespace
1038
+ })
1039
+ }
1040
+
1041
+ /**
1042
+ * 获取函数预置并发详情
1043
+ * @private
1044
+ * @param {IGetProvisionedConcurrencyConfig} concurrencyConfig
1045
+ * @returns {Promise<IGetProvisionedConcurrencyRes>}
1046
+ * @memberof FunctionService
1047
+ */
1048
+ @preLazy()
1049
+ public async getProvisionedConcurrencyConfig(
1050
+ concurrencyConfig: IGetProvisionedConcurrencyConfig
1051
+ ): Promise<IGetProvisionedConcurrencyRes> {
1052
+ const { namespace } = this.getFunctionConfig()
1053
+ const { functionName: FunctionName, qualifier: Qualifier } = concurrencyConfig
1054
+
1055
+ return this.scfService.request('GetProvisionedConcurrencyConfig', {
1056
+ FunctionName,
1057
+ Qualifier,
1058
+ Namespace: namespace
1059
+ })
1060
+ }
1061
+
1062
+ /**
1063
+ * 删除预置并发
1064
+ * @private
1065
+ * @param {IGetProvisionedConcurrencyConfig} concurrencyConfig
1066
+ * @returns {Promise<IResponseInfo>}
1067
+ * @memberof FunctionService
1068
+ */
1069
+ @preLazy()
1070
+ public async deleteProvisionedConcurrencyConfig(
1071
+ concurrencyConfig: IGetProvisionedConcurrencyConfig
1072
+ ): Promise<IResponseInfo> {
1073
+ const { namespace } = this.getFunctionConfig()
1074
+ const { functionName: FunctionName, qualifier: Qualifier } = concurrencyConfig
1075
+
1076
+ return this.scfService.request('DeleteProvisionedConcurrencyConfig', {
1077
+ FunctionName,
1078
+ Qualifier,
1079
+ Namespace: namespace
1080
+ })
1081
+ }
1082
+
1083
+ /**
1084
+ * 发布新版本
1085
+ * @param {IPublishVersionParams} publishParams
1086
+ * @returns {Promise<IPublishVersionRes>}
1087
+ * @memberof FunctionService
1088
+ */
1089
+ @preLazy()
1090
+ public async publishVersion(publishParams: IPublishVersionParams): Promise<IPublishVersionRes> {
1091
+ const { namespace } = this.getFunctionConfig()
1092
+ const { functionName: FunctionName, description: Description } = publishParams
1093
+
1094
+ return this.scfService.request('PublishVersion', {
1095
+ FunctionName,
1096
+ Description,
1097
+ Namespace: namespace
1098
+ })
1099
+ }
1100
+
1101
+ /**
1102
+ * 查询函数版本详情
1103
+ * @param {IListFunctionVersionParams} listVersionParams
1104
+ * @returns {Promise<IFunctionVersionsRes>}
1105
+ * @memberof FunctionService
1106
+ */
1107
+ @preLazy()
1108
+ public async listVersionByFunction(
1109
+ listVersionParams: IListFunctionVersionParams
1110
+ ): Promise<IFunctionVersionsRes> {
1111
+ const { namespace } = this.getFunctionConfig()
1112
+ const {
1113
+ functionName: FunctionName,
1114
+ offset: Offset,
1115
+ limit: Limit,
1116
+ order: Order,
1117
+ orderBy: OrderBy
1118
+ } = listVersionParams
1119
+
1120
+ return this.scfService.request('ListVersionByFunction', {
1121
+ FunctionName,
1122
+ Namespace: namespace,
1123
+ Offset,
1124
+ Limit,
1125
+ Order,
1126
+ OrderBy
1127
+ })
1128
+ }
1129
+
1130
+ /**
1131
+ *
1132
+ * @param {IUpdateFunctionAliasConfig} updateVersionConfigParams
1133
+ * @returns {Promise<IResponseInfo>}
1134
+ * @memberof FunctionService
1135
+ */
1136
+ @preLazy()
1137
+ public async updateFunctionAliasConfig(
1138
+ updateVersionConfigParams: IUpdateFunctionAliasConfig
1139
+ ): Promise<IResponseInfo> {
1140
+ const { namespace } = this.getFunctionConfig()
1141
+ const {
1142
+ functionName: FunctionName,
1143
+ name: Name,
1144
+ functionVersion: FunctionVersion,
1145
+ routingConfig: RoutingConfig,
1146
+ description: Description
1147
+ } = updateVersionConfigParams
1148
+
1149
+ return this.scfService.request('UpdateAlias', {
1150
+ FunctionName,
1151
+ Name,
1152
+ Namespace: namespace,
1153
+ FunctionVersion,
1154
+ RoutingConfig,
1155
+ Description
1156
+ })
1157
+ }
1158
+
1159
+ /**
1160
+ * 查询函数别名详情
1161
+ * @param {IGetFunctionAlias} params
1162
+ * @returns {Promise<IGetFunctionAliasRes>}
1163
+ * @memberof FunctionService
1164
+ */
1165
+ @preLazy()
1166
+ public async getFunctionAlias(params: IGetFunctionAlias): Promise<IGetFunctionAliasRes> {
1167
+ const { namespace } = this.getFunctionConfig()
1168
+ const { functionName: FunctionName, name: Name } = params
1169
+
1170
+ return this.scfService.request('GetAlias', {
1171
+ FunctionName,
1172
+ Name,
1173
+ Namespace: namespace
1174
+ })
1175
+ }
1176
+
1177
+ @preLazy()
1178
+ private async createAccessPath(name: string, path: string) {
1179
+ const access = this.environment.getAccessService()
1180
+ try {
1181
+ await access.createAccess({
1182
+ name,
1183
+ path
1184
+ })
1185
+ } catch (e) {
1186
+ // 当 Path 存在时,校验 Path 绑定的函数是不是当前函数
1187
+ if (e.code === 'InvalidParameter.APICreated') {
1188
+ const { APISet } = await access.getAccessList({
1189
+ name
1190
+ })
1191
+ if (APISet?.[0].Name !== name || APISet?.[0].Type !== 1) {
1192
+ throw e
1193
+ }
1194
+ } else {
1195
+ throw e
1196
+ }
1197
+ }
1198
+ }
1199
+
1200
+ @preLazy()
1201
+ private async getCodeParams(options: IFunctionCode, installDependency: 'TRUE' | 'FALSE') {
1202
+ const { func, functionPath, functionRootPath, base64Code } = options
1203
+ // 20MB
1204
+ const BIG_LENGTH = 167772160
1205
+ if (base64Code?.length > BIG_LENGTH) {
1206
+ throw new CloudBaseError('base64 不能大于 20 MB')
1207
+ }
1208
+
1209
+ if (base64Code?.length) {
1210
+ return {
1211
+ ZipFile: base64Code
1212
+ }
1213
+ }
1214
+
1215
+ const codeType: CodeType = func.runtime === 'Java8' ? CodeType.JavaFile : CodeType.File
1216
+ // 云端安装依赖,自动忽略 node_modules 目录
1217
+ const ignore =
1218
+ installDependency === 'TRUE'
1219
+ ? ['node_modules/**/*', 'node_modules', ...(func.ignore || [])]
1220
+ : [...(func.ignore || [])]
1221
+
1222
+ const packer = new FunctionPacker({
1223
+ ignore,
1224
+ codeType,
1225
+ functionPath,
1226
+ name: func.name,
1227
+ root: functionRootPath
1228
+ })
1229
+
1230
+ await packer.build()
1231
+
1232
+ // 通过云 API 传输的代码大小不能超过 50MB
1233
+ const reachMax = await packer.isReachMaxSize()
1234
+
1235
+ if (reachMax) {
1236
+ throw new CloudBaseError('函数代码不能大于 50MB')
1237
+ }
1238
+
1239
+ const base64 = await packer.getBase64Code()
1240
+ if (!base64?.length) {
1241
+ throw new CloudBaseError('文件不能为空')
1242
+ }
1243
+ return {
1244
+ ZipFile: base64
1245
+ }
1246
+ }
1247
+
1248
+ // 获取 COS 临时信息
1249
+ @preLazy()
1250
+ private async getTempCosInfo(name: string) {
1251
+ const { env, appId } = await this.getFunctionConfig()
1252
+
1253
+ /**
1254
+ * Response:
1255
+ * Date: "2020-03-18"
1256
+ * RequestId: "91876f56-7cd3-42bb-bc32-b74df5d0516e"
1257
+ * Sign: "Gc8QvXD50dx7yBfsl2yEYFwIL45hPTEyNTM2NjU4MTkm
1258
+ */
1259
+ return this.scfService.request('GetTempCosInfo', {
1260
+ ObjectPath: `${appId}/${env}/${name}.zip"`
1261
+ })
1262
+ }
1263
+
1264
+ private async retryCreateTrigger(name, triggers, count = 0) {
1265
+ try {
1266
+ const res = await this.createFunctionTriggers(name, triggers)
1267
+ return res
1268
+ } catch (e) {
1269
+ if (count < 3) {
1270
+ await sleep(500)
1271
+ const res = await this.retryCreateTrigger(name, triggers, count + 1)
1272
+ return res
1273
+ } else {
1274
+ throw e
1275
+ }
1276
+ }
1277
+ }
1278
+
1279
+ /**
1280
+ * 获取函数配置信息
1281
+ * @private
1282
+ * @returns
1283
+ * @memberof FunctionService
1284
+ */
1285
+ private getFunctionConfig() {
1286
+ const envConfig = this.environment.lazyEnvironmentConfig
1287
+ const namespace = envConfig.Functions[0].Namespace
1288
+ const appId = envConfig.Storages[0]?.AppId
1289
+ const { proxy } = this.environment.cloudBaseContext
1290
+
1291
+ return {
1292
+ proxy,
1293
+ appId,
1294
+ namespace,
1295
+ env: envConfig.EnvId
1296
+ }
1297
+ }
1298
+
1299
+ /**
1300
+ * 获取 vpc 信息
1301
+ * @returns
1302
+ */
1303
+ private async getVpcs() {
1304
+ const { VpcSet } = await this.vpcService.request('DescribeVpcs')
1305
+ return VpcSet
1306
+ }
1307
+
1308
+ /**
1309
+ * 获取子网
1310
+ * @param {string} vpcId
1311
+ * @returns
1312
+ */
1313
+ private async getSubnets(vpcId: string) {
1314
+ const { SubnetSet } = await this.vpcService.request('DescribeSubnets', {
1315
+ Filters: [
1316
+ {
1317
+ Name: 'vpc-id',
1318
+ Values: [vpcId]
1319
+ }
1320
+ ]
1321
+ })
1322
+ return SubnetSet
1323
+ }
1324
+
1325
+ // 检查函数状态,部分操作在函数更新中时不可进行
1326
+ private async waitFunctionActive(funcName: string, codeSecret?: string) {
1327
+ let ticker
1328
+ let timer
1329
+ let resolved
1330
+
1331
+ return new Promise<void>((resolve, reject) => {
1332
+ // 超时时间 5 分钟
1333
+ timer = setTimeout(() => {
1334
+ clearInterval(ticker)
1335
+ if (!resolved) {
1336
+ reject(new CloudBaseError('函数状态异常,检查超时'))
1337
+ }
1338
+ }, 300000)
1339
+
1340
+ ticker = setInterval(async () => {
1341
+ try {
1342
+ const { Status } = await this.getFunctionDetail(funcName, codeSecret)
1343
+ // 更新中
1344
+ if (Status === SCF_STATUS.CREATING || Status === SCF_STATUS.UPDATING) return
1345
+ // 创建失败
1346
+ if (Status === SCF_STATUS.CREATE_FAILED) {
1347
+ throw new CloudBaseError('云函数创建失败')
1348
+ }
1349
+ // 函数状态正常
1350
+ clearInterval(ticker)
1351
+ clearTimeout(timer)
1352
+ resolve()
1353
+ } catch (e) {
1354
+ clearInterval(ticker)
1355
+ clearTimeout(timer)
1356
+ reject(e)
1357
+ }
1358
+ resolved = true
1359
+ }, 1000)
1360
+ })
1361
+ }
1362
+ }