@cloudbase/manager-node 4.2.0 → 4.2.2

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