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