@cloudbase/app 2.26.1 → 2.26.3
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.
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +43 -7
- package/dist/cjs/index.node.d.ts +6 -0
- package/dist/cjs/index.node.js +20 -0
- package/dist/cjs/libs/adapter-node/constants.d.ts +33 -0
- package/dist/cjs/libs/adapter-node/constants.js +38 -0
- package/dist/cjs/libs/adapter-node/context.d.ts +13 -0
- package/dist/cjs/libs/adapter-node/context.js +162 -0
- package/dist/cjs/libs/adapter-node/index.d.ts +23 -0
- package/dist/cjs/libs/adapter-node/index.js +124 -0
- package/dist/cjs/libs/adapter-node/metadata.d.ts +14 -0
- package/dist/cjs/libs/adapter-node/metadata.js +130 -0
- package/dist/cjs/libs/adapter-node/request.d.ts +38 -0
- package/dist/cjs/libs/adapter-node/request.js +441 -0
- package/dist/cjs/libs/adapter-node/tool.d.ts +2 -0
- package/dist/cjs/libs/adapter-node/tool.js +228 -0
- package/dist/cjs/libs/adapter-node/types.d.ts +78 -0
- package/dist/cjs/libs/adapter-node/types.js +3 -0
- package/dist/cjs/libs/adapter-node/utils.d.ts +17 -0
- package/dist/cjs/libs/adapter-node/utils.js +221 -0
- package/dist/cjs/libs/request.d.ts +1 -0
- package/dist/cjs/libs/request.js +34 -19
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +43 -7
- package/dist/esm/index.node.d.ts +6 -0
- package/dist/esm/index.node.js +12 -0
- package/dist/esm/libs/adapter-node/constants.d.ts +33 -0
- package/dist/esm/libs/adapter-node/constants.js +35 -0
- package/dist/esm/libs/adapter-node/context.d.ts +13 -0
- package/dist/esm/libs/adapter-node/context.js +156 -0
- package/dist/esm/libs/adapter-node/index.d.ts +23 -0
- package/dist/esm/libs/adapter-node/index.js +118 -0
- package/dist/esm/libs/adapter-node/metadata.d.ts +14 -0
- package/dist/esm/libs/adapter-node/metadata.js +123 -0
- package/dist/esm/libs/adapter-node/request.d.ts +38 -0
- package/dist/esm/libs/adapter-node/request.js +438 -0
- package/dist/esm/libs/adapter-node/tool.d.ts +2 -0
- package/dist/esm/libs/adapter-node/tool.js +224 -0
- package/dist/esm/libs/adapter-node/types.d.ts +78 -0
- package/dist/esm/libs/adapter-node/types.js +2 -0
- package/dist/esm/libs/adapter-node/utils.d.ts +17 -0
- package/dist/esm/libs/adapter-node/utils.js +205 -0
- package/dist/esm/libs/request.d.ts +1 -0
- package/dist/esm/libs/request.js +34 -19
- package/dist/miniprogram/index.js +1 -1
- package/package.json +32 -4
- package/src/index.node.ts +22 -0
- package/src/index.ts +91 -7
- package/src/libs/adapter-node/constants.ts +42 -0
- package/src/libs/adapter-node/context.ts +238 -0
- package/src/libs/adapter-node/index.ts +166 -0
- package/src/libs/adapter-node/metadata.ts +69 -0
- package/src/libs/adapter-node/request.ts +486 -0
- package/src/libs/adapter-node/tool.ts +223 -0
- package/src/libs/adapter-node/types.ts +116 -0
- package/src/libs/adapter-node/utils.ts +182 -0
- package/src/libs/request.ts +125 -113
- package/webpack/web.prod.js +14 -13
- package/webpack/webpack.miniprogram.js +10 -3
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/app",
|
|
3
|
-
"version": "2.26.
|
|
3
|
+
"version": "2.26.3",
|
|
4
4
|
"description": "cloudbase javascript sdk core",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"node": {
|
|
10
|
+
"import": "./dist/esm/index.node.js",
|
|
11
|
+
"require": "./dist/cjs/index.node.js"
|
|
12
|
+
},
|
|
13
|
+
"import": "./dist/esm/index.js",
|
|
14
|
+
"require": "./dist/cjs/index.js",
|
|
15
|
+
"default": "./dist/cjs/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
7
19
|
"scripts": {
|
|
8
20
|
"tsc": "rm -rf dist/ && tsc -p tsconfig.esm.json && tsc -p tsconfig.json",
|
|
9
21
|
"lint": "eslint --fix \"./src/**/*.ts\"",
|
|
@@ -31,8 +43,24 @@
|
|
|
31
43
|
"dependencies": {
|
|
32
44
|
"@cloudbase/adapter-interface": "^0.7.1",
|
|
33
45
|
"@cloudbase/adapter-wx_mp": "^1.3.1",
|
|
34
|
-
"@cloudbase/types": "2.26.
|
|
35
|
-
"@cloudbase/utilities": "2.26.
|
|
46
|
+
"@cloudbase/types": "2.26.3",
|
|
47
|
+
"@cloudbase/utilities": "2.26.3"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@cloudbase/signature-nodejs": ">=1.0.0",
|
|
51
|
+
"jsonwebtoken": ">=8.0.0",
|
|
52
|
+
"ws": "^8.18.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@cloudbase/signature-nodejs": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"jsonwebtoken": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"ws": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
36
64
|
},
|
|
37
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "de5d8b333e373ee2db4a079bb70f80639e78e590"
|
|
38
66
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js 入口
|
|
3
|
+
* 引用 base 模块的全部导出,并额外加载 Node adapter
|
|
4
|
+
*/
|
|
5
|
+
import cloudbase from './index'
|
|
6
|
+
|
|
7
|
+
export { getBaseEndPoint } from './constants/common'
|
|
8
|
+
export { LANGS } from './libs/lang'
|
|
9
|
+
export type { Cloudbase } from './index'
|
|
10
|
+
export { cloudbase }
|
|
11
|
+
|
|
12
|
+
// Node 环境下加载 Node adapter
|
|
13
|
+
try {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
|
|
15
|
+
const nodeAdapter = require('./libs/adapter-node').default
|
|
16
|
+
cloudbase.useAdapters(nodeAdapter)
|
|
17
|
+
} catch (error) {
|
|
18
|
+
// throw error
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 默认导出实例
|
|
22
|
+
export default cloudbase
|
package/src/index.ts
CHANGED
|
@@ -101,24 +101,31 @@ class Cloudbase implements ICloudbase {
|
|
|
101
101
|
],
|
|
102
102
|
})
|
|
103
103
|
public init(config: ICloudbaseConfig & { lang?: LANGS }): Cloudbase {
|
|
104
|
+
config.endPointMode = config.endPointMode || 'CLOUD_API'
|
|
105
|
+
|
|
106
|
+
// 初始化时若未兼容平台,则使用默认adapter
|
|
107
|
+
if (!Platform.adapter) {
|
|
108
|
+
this.useDefaultAdapter()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 在判断config.env之前,先处理node环境的配置
|
|
112
|
+
config = this.dealNodeAdapterConfig(config)
|
|
113
|
+
|
|
104
114
|
if (!config.env) {
|
|
105
115
|
throw new Error(JSON.stringify({
|
|
106
116
|
code: ERRORS.INVALID_PARAMS,
|
|
107
117
|
msg: 'env must not be specified',
|
|
108
118
|
}),)
|
|
109
119
|
}
|
|
110
|
-
// config.endPointMode = config.endPointMode || 'GATEWAY'
|
|
111
120
|
|
|
112
|
-
|
|
113
|
-
if (!Platform.adapter) {
|
|
114
|
-
this.useDefaultAdapter()
|
|
115
|
-
}
|
|
121
|
+
this.isInitialized(config)
|
|
116
122
|
|
|
117
|
-
const reqConfig: IRequestConfig = {
|
|
118
|
-
timeout: config.timeout ||
|
|
123
|
+
const reqConfig: IRequestConfig & { auth: ICloudbaseConfig['auth'] } = {
|
|
124
|
+
timeout: config.timeout || 15000,
|
|
119
125
|
timeoutMsg: `[${getSdkName()}][REQUEST TIMEOUT] request had been abort since didn't finished within${
|
|
120
126
|
config.timeout / 1000
|
|
121
127
|
}s`,
|
|
128
|
+
auth: config.auth,
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
this.requestClient = new Platform.adapter.reqClass(reqConfig)
|
|
@@ -148,12 +155,18 @@ class Cloudbase implements ICloudbase {
|
|
|
148
155
|
_fromApp: app,
|
|
149
156
|
i18n,
|
|
150
157
|
endPointMode: config.endPointMode,
|
|
158
|
+
auth: config.auth,
|
|
151
159
|
})
|
|
152
160
|
app.requestClient = this.requestClient
|
|
153
161
|
;(this as any)?.fire?.('cloudbase_init', app)
|
|
154
162
|
|
|
155
163
|
this.try2InitAuth(app)
|
|
156
164
|
|
|
165
|
+
// node环境可以从adapter获取nodeTool的方法
|
|
166
|
+
if (Platform.adapter?.nodeTool) {
|
|
167
|
+
Platform.adapter?.nodeTool(app, this.cloudbaseConfig)
|
|
168
|
+
}
|
|
169
|
+
|
|
157
170
|
return app
|
|
158
171
|
}
|
|
159
172
|
|
|
@@ -218,6 +231,43 @@ class Cloudbase implements ICloudbase {
|
|
|
218
231
|
setSdkName(name)
|
|
219
232
|
}
|
|
220
233
|
|
|
234
|
+
/**
|
|
235
|
+
* 检查当前 Cloudbase 实例是否已完成初始化
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```javascript
|
|
239
|
+
* import cloudbase from '@cloudbase/js-sdk'
|
|
240
|
+
*
|
|
241
|
+
* // 初始化前
|
|
242
|
+
* console.log(cloudbase.isInitialized()) // false
|
|
243
|
+
*
|
|
244
|
+
* const app = cloudbase.init({ env: 'your-env-id' })
|
|
245
|
+
*
|
|
246
|
+
* // 初始化后
|
|
247
|
+
* console.log(app.isInitialized()) // true
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* @returns {boolean} 是否已初始化
|
|
251
|
+
*/
|
|
252
|
+
public isInitialized(config = this.cloudbaseConfig): boolean {
|
|
253
|
+
// 检测常见占位符 env 值,在开发阶段给出友好提示
|
|
254
|
+
const PLACEHOLDER_PATTERNS = [
|
|
255
|
+
'your-env-id', 'your-envid', 'your_env_id',
|
|
256
|
+
'xxx-yyy', 'xxxx-yyy', 'envId', 'env-id',
|
|
257
|
+
'your-environment-id', 'REPLACE_ME',
|
|
258
|
+
]
|
|
259
|
+
if (PLACEHOLDER_PATTERNS.some(p => config.env.toLowerCase() === p.toLowerCase())) {
|
|
260
|
+
printWarn(
|
|
261
|
+
ERRORS.INVALID_PARAMS,
|
|
262
|
+
`[CloudBase] 检测到环境 ID "${config.env}" 可能是占位符,请替换为真实的环境 ID。\n`
|
|
263
|
+
+ ' 获取方式:登录腾讯云开发平台 → 环境管理 → 环境设置 → 环境 ID\n'
|
|
264
|
+
+ ' 建议通过环境变量配置:process.env.CLOUDBASE_ENV 或 import.meta.env.VITE_CLOUDBASE_ENV',
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return !!(config?.env)
|
|
269
|
+
}
|
|
270
|
+
|
|
221
271
|
/** 设置 tcb api 的 endpoint */
|
|
222
272
|
public registerEndPoint(url: string, protocol?: 'http' | 'https') {
|
|
223
273
|
setEndPointInfo({ baseUrl: url, protocol, env: this.config.env, endPointKey: 'CLOUD_API' })
|
|
@@ -273,6 +323,40 @@ class Cloudbase implements ICloudbase {
|
|
|
273
323
|
console.log('try2InitAuth error:', error)
|
|
274
324
|
}
|
|
275
325
|
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* 处理 Node 环境下 adapter 的配置信息
|
|
329
|
+
* 在 Node 环境中,通过 adapter 自动获取密钥、环境ID、accessKey 等配置,
|
|
330
|
+
* 减少用户手动传入配置的负担
|
|
331
|
+
* @param config - 云开发初始化配置对象
|
|
332
|
+
* @returns 补充了 Node adapter 信息后的配置对象
|
|
333
|
+
*/
|
|
334
|
+
private dealNodeAdapterConfig(config: ICloudbaseConfig) {
|
|
335
|
+
// node环境可以从adapter获取默认的secretId和secretKey
|
|
336
|
+
if (typeof process !== 'undefined' && typeof process.env !== 'undefined' && !process.env.IS_BROWSER_BUILD && Platform.adapter?.getSecretInfo) {
|
|
337
|
+
const secretInfo = Platform.adapter?.getSecretInfo(config)
|
|
338
|
+
// 将 adapter 提供的密钥信息写入 config.auth
|
|
339
|
+
config.auth = {
|
|
340
|
+
...config.auth,
|
|
341
|
+
secretId: secretInfo.secretId,
|
|
342
|
+
secretKey: secretInfo.secretKey,
|
|
343
|
+
sessionToken: secretInfo.sessionToken,
|
|
344
|
+
secretType: secretInfo.secretType as any,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// node 环境可能可以从上下文获取env,用户未显式指定时自动填充
|
|
348
|
+
if (!config.env) {
|
|
349
|
+
config.env = secretInfo.env
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// node adapter环境能读取到 process.env.CLOUDBASE_APIKEY 时,在没有传入accessKey时,使用 node adapter 提供的 accessKey(process.env.CLOUDBASE_APIKEY)
|
|
353
|
+
if (!config.accessKey && secretInfo.accessKey) {
|
|
354
|
+
config.accessKey = secretInfo.accessKey
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return config
|
|
359
|
+
}
|
|
276
360
|
}
|
|
277
361
|
|
|
278
362
|
// 类型导出
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
/** 统一错误码定义 */
|
|
3
|
+
export const ERROR = {
|
|
4
|
+
INVALID_PARAM: {
|
|
5
|
+
code: 'INVALID_PARAM',
|
|
6
|
+
message: 'invalid param',
|
|
7
|
+
},
|
|
8
|
+
SYS_ERR: {
|
|
9
|
+
code: 'SYS_ERR',
|
|
10
|
+
message: 'system error',
|
|
11
|
+
},
|
|
12
|
+
STORAGE_REQUEST_FAIL: {
|
|
13
|
+
code: 'STORAGE_REQUEST_FAIL',
|
|
14
|
+
message: 'storage request fail',
|
|
15
|
+
},
|
|
16
|
+
STORAGE_FILE_NONEXIST: {
|
|
17
|
+
code: 'STORAGE_FILE_NONEXIST',
|
|
18
|
+
message: 'storage file not exist',
|
|
19
|
+
},
|
|
20
|
+
TCB_CLS_UNOPEN: {
|
|
21
|
+
code: 'TCB_CLS_UNOPEN',
|
|
22
|
+
message: '需要先开通日志检索功能',
|
|
23
|
+
},
|
|
24
|
+
INVALID_CONTEXT: {
|
|
25
|
+
code: 'INVALID_CONTEXT',
|
|
26
|
+
message: '无效的context对象,请使用 云函数入口的context参数',
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 管理端 API 路径,用于替换 /web 路径(需要密钥签名时) */
|
|
31
|
+
export const ADMIN_PATH = '/admin'
|
|
32
|
+
|
|
33
|
+
/** 管理端获取客户端凭证 access_token,为管理员权限 */
|
|
34
|
+
export const CLIENT_AUTH_PATH = '/auth/v1/token/clientCredential'
|
|
35
|
+
|
|
36
|
+
/** 自定义用户 ID 校验规则:4~32 位,允许字母、数字及部分特殊字符 */
|
|
37
|
+
export const checkCustomUserIdRegex = /^[a-zA-Z0-9_\-#@~=*(){}[\]:.,<>+]{4,32}$/
|
|
38
|
+
|
|
39
|
+
export enum SECRET_TYPE {
|
|
40
|
+
'SECRET' = 'SECRET',
|
|
41
|
+
'SESSION_SECRET' = 'SESSION_SECRET',
|
|
42
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/* eslint-disable import/no-mutable-exports */
|
|
2
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
3
|
+
/* eslint-disable no-restricted-syntax */
|
|
4
|
+
import { ERROR, SECRET_TYPE } from './constants'
|
|
5
|
+
import { checkIsInScf, getEnv } from './utils'
|
|
6
|
+
import type { IContextParam, ICompleteCloudbaseContext } from './types'
|
|
7
|
+
import { ICloudbaseConfig } from '@cloudbase/types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 解析并校验云函数入口的 Context 参数
|
|
11
|
+
* 将下划线命名的字段转为驼峰格式,并解析环境变量
|
|
12
|
+
*
|
|
13
|
+
* @param context - 云函数入口传入的 context 对象
|
|
14
|
+
* @returns 解析后的上下文对象,包含 environment(新架构)或 environ(老架构)
|
|
15
|
+
* @throws INVALID_CONTEXT - context 不是对象或解析失败时
|
|
16
|
+
*/
|
|
17
|
+
export function parseContext(context: IContextParam) {
|
|
18
|
+
if (typeof context !== 'object') {
|
|
19
|
+
throw { ...ERROR.INVALID_CONTEXT, message: 'context 必须为对象类型' }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const parseResult: any = {}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
memory_limit_in_mb,
|
|
26
|
+
time_limit_in_ms,
|
|
27
|
+
request_id,
|
|
28
|
+
function_version,
|
|
29
|
+
namespace,
|
|
30
|
+
function_name,
|
|
31
|
+
|
|
32
|
+
environ,
|
|
33
|
+
environment,
|
|
34
|
+
} = context
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
parseResult.memoryLimitInMb = memory_limit_in_mb
|
|
38
|
+
parseResult.timeLimitIns = time_limit_in_ms
|
|
39
|
+
parseResult.requestId = request_id
|
|
40
|
+
parseResult.functionVersion = function_version
|
|
41
|
+
parseResult.namespace = namespace
|
|
42
|
+
parseResult.functionName = function_name
|
|
43
|
+
|
|
44
|
+
if (environment) {
|
|
45
|
+
// 新架构:environment 为 JSON 字符串,直接解析
|
|
46
|
+
parseResult.environment = JSON.parse(environment)
|
|
47
|
+
} else {
|
|
48
|
+
// TODO: 考虑移除老架构的兼容逻辑
|
|
49
|
+
// 老架构:environ 为分号分隔的 key=value 字符串
|
|
50
|
+
// 存在 bug:value 含特殊字符时可能影响解析
|
|
51
|
+
const parseEnviron: any = environ?.split(';')
|
|
52
|
+
const parseEnvironObj: any = {}
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-for-in-array
|
|
55
|
+
for (const i in parseEnviron) {
|
|
56
|
+
if (parseEnviron[i].includes('=')) {
|
|
57
|
+
// 只按第一个 = 切割,保留 value 中的 =
|
|
58
|
+
const equalIndex = parseEnviron[i].indexOf('=')
|
|
59
|
+
const key = parseEnviron[i].slice(0, equalIndex)
|
|
60
|
+
let value: any = parseEnviron[i].slice(equalIndex + 1)
|
|
61
|
+
|
|
62
|
+
// value 含逗号时视为数组
|
|
63
|
+
if (value.indexOf(',') >= 0) {
|
|
64
|
+
value = value.split(',')
|
|
65
|
+
}
|
|
66
|
+
parseEnvironObj[key] = value
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
parseResult.environ = parseEnvironObj
|
|
71
|
+
}
|
|
72
|
+
} catch (_err: any) {
|
|
73
|
+
throw { ...ERROR.INVALID_CONTEXT }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return parseResult
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 获取当前云函数内的所有环境变量
|
|
81
|
+
* 统一从 process.env 和 context 两个来源合并取值
|
|
82
|
+
*
|
|
83
|
+
* 取值优先级:context > process.env
|
|
84
|
+
* 通过 TCB_CONTEXT_KEYS / WX_CONTEXT_KEYS 动态扩展需要读取的环境变量列表
|
|
85
|
+
*
|
|
86
|
+
* @param context - 可选,云函数入口的 context 参数
|
|
87
|
+
* @returns 合并后的完整环境变量上下文
|
|
88
|
+
*/
|
|
89
|
+
export const getCloudbaseContext = (context?: IContextParam): ICompleteCloudbaseContext => {
|
|
90
|
+
// 云函数环境下校验必要环境变量
|
|
91
|
+
if (checkIsInScf()) {
|
|
92
|
+
if (!getEnv('TENCENTCLOUD_REGION')) {
|
|
93
|
+
console.error('[TCB][ERROR] missing `TENCENTCLOUD_REGION` environment')
|
|
94
|
+
}
|
|
95
|
+
if (!getEnv('SCF_NAMESPACE')) {
|
|
96
|
+
console.error('[TCB][ERROR] missing `SCF_NAMESPACE` environment')
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 第一步:从 process.env 读取基础环境变量
|
|
101
|
+
const envObj = getEnv()
|
|
102
|
+
const envFromProcessEnv: any = {
|
|
103
|
+
TRIGGER_SRC: envObj.TRIGGER_SRC,
|
|
104
|
+
_SCF_TCB_LOG: envObj._SCF_TCB_LOG,
|
|
105
|
+
SCF_NAMESPACE: envObj.SCF_NAMESPACE,
|
|
106
|
+
TENCENTCLOUD_RUNENV: envObj.TENCENTCLOUD_RUNENV,
|
|
107
|
+
TENCENTCLOUD_SECRETID: envObj.TENCENTCLOUD_SECRETID,
|
|
108
|
+
TENCENTCLOUD_SECRETKEY: envObj.TENCENTCLOUD_SECRETKEY,
|
|
109
|
+
TENCENTCLOUD_SESSIONTOKEN: envObj.TENCENTCLOUD_SESSIONTOKEN,
|
|
110
|
+
WX_CONTEXT_KEYS: envObj.WX_CONTEXT_KEYS,
|
|
111
|
+
WX_TRIGGER_API_TOKEN_V0: envObj.WX_TRIGGER_API_TOKEN_V0,
|
|
112
|
+
WX_CLIENTIP: envObj.WX_CLIENTIP,
|
|
113
|
+
WX_CLIENTIPV6: envObj.WX_CLIENTIPV6,
|
|
114
|
+
TCB_CONTEXT_KEYS: envObj.TCB_CONTEXT_KEYS,
|
|
115
|
+
TCB_CONTEXT_CNFG: envObj.TCB_CONTEXT_CNFG,
|
|
116
|
+
LOGINTYPE: envObj.LOGINTYPE,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 第二步:从 context 中解析环境变量
|
|
120
|
+
let envFromContext: any = {}
|
|
121
|
+
if (context) {
|
|
122
|
+
const { environment, environ } = parseContext(context)
|
|
123
|
+
envFromContext = environment || environ || {}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 第三步:根据 CONTEXT_KEYS 动态扩展变量列表(优先取 context 中的值)
|
|
127
|
+
const tcbContextKeys = envFromContext.TCB_CONTEXT_KEYS || envObj.TCB_CONTEXT_KEYS
|
|
128
|
+
const wxContextKeys = envFromContext.WX_CONTEXT_KEYS || envObj.WX_CONTEXT_KEYS
|
|
129
|
+
|
|
130
|
+
if (tcbContextKeys) {
|
|
131
|
+
try {
|
|
132
|
+
const tcbKeysList = tcbContextKeys.split(',')
|
|
133
|
+
for (const item of tcbKeysList) {
|
|
134
|
+
envFromProcessEnv[item] = envFromContext[item] || getEnv(item)
|
|
135
|
+
}
|
|
136
|
+
} catch (_e) {
|
|
137
|
+
//
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (wxContextKeys) {
|
|
142
|
+
try {
|
|
143
|
+
const wxKeysList = wxContextKeys.split(',')
|
|
144
|
+
for (const item of wxKeysList) {
|
|
145
|
+
envFromProcessEnv[item] = envFromContext[item] || getEnv(item)
|
|
146
|
+
}
|
|
147
|
+
} catch (_e) {
|
|
148
|
+
//
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 第四步:合并所有来源,context 的值覆盖 process.env,过滤 undefined
|
|
153
|
+
const allContext = { ...envFromProcessEnv, ...envFromContext }
|
|
154
|
+
|
|
155
|
+
const finalContext: any = {}
|
|
156
|
+
for (const key in allContext) {
|
|
157
|
+
if (allContext[key] !== undefined) {
|
|
158
|
+
finalContext[key] = allContext[key]
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return finalContext
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** 缓存 extendedContext,供其他模块读取(如 tool.ts 中获取 userId) */
|
|
166
|
+
export let INIT_CONFIG: ICloudbaseConfig = {} as unknown as ICloudbaseConfig
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 从环境变量中获取腾讯云临时密钥信息
|
|
170
|
+
* 用于 API 请求签名
|
|
171
|
+
*/
|
|
172
|
+
export const getSecretInfo = (config?: ICloudbaseConfig) => {
|
|
173
|
+
INIT_CONFIG = JSON.parse(JSON.stringify(config || {}))
|
|
174
|
+
|
|
175
|
+
const { TCB_ENV, SCF_NAMESPACE } = getCloudbaseContext()
|
|
176
|
+
|
|
177
|
+
let env = config?.env || TCB_ENV || SCF_NAMESPACE || ''
|
|
178
|
+
const defaultInfo = {
|
|
179
|
+
env,
|
|
180
|
+
secretId: '',
|
|
181
|
+
secretKey: '',
|
|
182
|
+
sessionToken: '',
|
|
183
|
+
accessKey: '',
|
|
184
|
+
secretType: '',
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (config?.accessKey) {
|
|
188
|
+
return defaultInfo
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (config?.auth?.secretId && config?.auth?.secretKey) {
|
|
192
|
+
return {
|
|
193
|
+
...defaultInfo,
|
|
194
|
+
secretId: config.auth.secretId,
|
|
195
|
+
secretKey: config.auth.secretKey,
|
|
196
|
+
secretType: SECRET_TYPE.SECRET,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const secretEnv = getEnv()
|
|
201
|
+
const { TENCENTCLOUD_SECRETID } = secretEnv
|
|
202
|
+
const { TENCENTCLOUD_SECRETKEY } = secretEnv
|
|
203
|
+
const { TENCENTCLOUD_SESSIONTOKEN } = secretEnv
|
|
204
|
+
const { CLOUDBASE_APIKEY } = secretEnv
|
|
205
|
+
|
|
206
|
+
if (CLOUDBASE_APIKEY) {
|
|
207
|
+
return {
|
|
208
|
+
...defaultInfo,
|
|
209
|
+
accessKey: CLOUDBASE_APIKEY,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (TENCENTCLOUD_SECRETID && TENCENTCLOUD_SECRETKEY) {
|
|
214
|
+
return {
|
|
215
|
+
...defaultInfo,
|
|
216
|
+
secretId: TENCENTCLOUD_SECRETID,
|
|
217
|
+
secretKey: TENCENTCLOUD_SECRETKEY,
|
|
218
|
+
sessionToken: TENCENTCLOUD_SESSIONTOKEN || '',
|
|
219
|
+
secretType: SECRET_TYPE.SESSION_SECRET,
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (config?.context?.extendedContext) {
|
|
224
|
+
const { extendedContext } = config.context
|
|
225
|
+
if (!env) {
|
|
226
|
+
env = extendedContext.envId || ''
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
...defaultInfo,
|
|
231
|
+
secretId: extendedContext?.tmpSecret?.secretId || '',
|
|
232
|
+
secretKey: extendedContext?.tmpSecret?.secretKey || '',
|
|
233
|
+
sessionToken: extendedContext?.tmpSecret?.token || '',
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return { ...defaultInfo }
|
|
238
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* eslint-disable prefer-destructuring */
|
|
2
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
3
|
+
import {
|
|
4
|
+
SDKAdapterInterface,
|
|
5
|
+
StorageInterface,
|
|
6
|
+
StorageType,
|
|
7
|
+
WebSocketContructor as WebSocketConstructor,
|
|
8
|
+
} from '@cloudbase/adapter-interface'
|
|
9
|
+
|
|
10
|
+
import { NodeRequest } from './request'
|
|
11
|
+
import { nodeTool } from './tool'
|
|
12
|
+
import { getSecretInfo } from './context'
|
|
13
|
+
import { parseQueryString } from './utils'
|
|
14
|
+
import { ICloudbaseConfig } from '@cloudbase/types'
|
|
15
|
+
|
|
16
|
+
// ws 延迟加载,避免非 Node 环境打包时引入
|
|
17
|
+
let _WS: any = null
|
|
18
|
+
function getWS() {
|
|
19
|
+
if (!_WS) {
|
|
20
|
+
try {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
|
|
22
|
+
_WS = require('ws')
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error('缺少依赖 ws,请执行以下命令安装:\n\n'
|
|
25
|
+
+ ' npm install ws\n\n'
|
|
26
|
+
+ '该依赖用于 Node 环境下的 WebSocket 连接。',)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return _WS
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Re-export for external consumers
|
|
33
|
+
export { NodeRequest } from './request'
|
|
34
|
+
export { parseQueryString, createWebStreamFromNodeReadableStream } from './utils'
|
|
35
|
+
export type {
|
|
36
|
+
ICreateTicketOpts,
|
|
37
|
+
IGetUserInfoResult,
|
|
38
|
+
IGetEndUserInfoResult,
|
|
39
|
+
IUserInfoQuery,
|
|
40
|
+
IContextParam,
|
|
41
|
+
ICompleteCloudbaseContext,
|
|
42
|
+
} from './types'
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 环境匹配检测:判断当前运行环境是否为 Node.js
|
|
46
|
+
* SDK 通过此方法决定是否使用当前 adapter
|
|
47
|
+
*/
|
|
48
|
+
function isMatch(): boolean {
|
|
49
|
+
// eslint-disable-next-line eqeqeq
|
|
50
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 基于 Map 的内存存储实现
|
|
55
|
+
* Node.js 环境下无浏览器 Storage API,使用内存 Map 模拟
|
|
56
|
+
*/
|
|
57
|
+
const storage: StorageInterface = (() => {
|
|
58
|
+
const db = new Map()
|
|
59
|
+
return {
|
|
60
|
+
mode: 'sync',
|
|
61
|
+
setItem(key, value) {
|
|
62
|
+
db.set(key, value)
|
|
63
|
+
},
|
|
64
|
+
getItem(key) {
|
|
65
|
+
return db.get(key)
|
|
66
|
+
},
|
|
67
|
+
removeItem(key) {
|
|
68
|
+
db.delete(key)
|
|
69
|
+
},
|
|
70
|
+
clear() {
|
|
71
|
+
db.clear()
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
})()
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 生成 Node.js 环境的 SDK Adapter
|
|
78
|
+
* 组装请求类、存储、WebSocket、认证等能力
|
|
79
|
+
*
|
|
80
|
+
* @param options - 配置项,包含 EventBus(用于验证码回调)
|
|
81
|
+
* @returns SDK Adapter 实例
|
|
82
|
+
*/
|
|
83
|
+
function genAdapter(options: any) {
|
|
84
|
+
const adapter: SDKAdapterInterface & {
|
|
85
|
+
getSecretInfo: (config?: ICloudbaseConfig) => {
|
|
86
|
+
env: string
|
|
87
|
+
secretId: string
|
|
88
|
+
secretKey: string
|
|
89
|
+
sessionToken: string
|
|
90
|
+
accessKey: string
|
|
91
|
+
secretType: string
|
|
92
|
+
}
|
|
93
|
+
nodeTool: (app: any, config: ICloudbaseConfig) => void
|
|
94
|
+
} = {
|
|
95
|
+
nodeTool,
|
|
96
|
+
getSecretInfo,
|
|
97
|
+
root: { globalThis: {} },
|
|
98
|
+
reqClass: NodeRequest,
|
|
99
|
+
wsClass: getWS() as WebSocketConstructor,
|
|
100
|
+
sessionStorage: storage,
|
|
101
|
+
localStorage: storage,
|
|
102
|
+
primaryStorage: StorageType.session,
|
|
103
|
+
captchaOptions: {
|
|
104
|
+
/**
|
|
105
|
+
* 验证码处理回调
|
|
106
|
+
* 解析 data: URI 中的验证码参数,通过 EventBus 通知业务层
|
|
107
|
+
* 等待业务层返回验证结果后 resolve
|
|
108
|
+
*/
|
|
109
|
+
openURIWithCallback: (_url: string) => {
|
|
110
|
+
const { EventBus } = options
|
|
111
|
+
let queryObj: Record<string, string | string[]> = {}
|
|
112
|
+
let url = _url
|
|
113
|
+
|
|
114
|
+
// 从 data: URI 中提取查询参数
|
|
115
|
+
const matched = _url.match(/^(data:.*?)(\?[^#\s]*)?$/)
|
|
116
|
+
if (matched) {
|
|
117
|
+
url = matched[1]
|
|
118
|
+
const search = matched[2]
|
|
119
|
+
if (search) {
|
|
120
|
+
queryObj = parseQueryString(search)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const { token, ...restQueryObj } = queryObj
|
|
125
|
+
|
|
126
|
+
// data: 协议但无 token,视为无效验证码数据
|
|
127
|
+
if (/^data:/.test(url) && !token) {
|
|
128
|
+
return Promise.reject({
|
|
129
|
+
error: 'invalid_argument',
|
|
130
|
+
error_description: `invalie captcha data: ${_url}`,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
if (!token) {
|
|
134
|
+
return Promise.reject({
|
|
135
|
+
error: 'unimplemented',
|
|
136
|
+
error_description: 'need to impl captcha data',
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return new Promise((resolve) => {
|
|
141
|
+
// 通知业务层展示验证码
|
|
142
|
+
EventBus.$emit('CAPTCHA_DATA_CHANGE', {
|
|
143
|
+
...restQueryObj,
|
|
144
|
+
token,
|
|
145
|
+
url,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// 等待业务层返回验证结果
|
|
149
|
+
EventBus.$once('RESOLVE_CAPTCHA_DATA', (res: { captcha_token: string; expires_in: number }) => {
|
|
150
|
+
resolve(res)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
return adapter
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Node.js Adapter 入口导出 */
|
|
160
|
+
const adapter = {
|
|
161
|
+
genAdapter,
|
|
162
|
+
isMatch,
|
|
163
|
+
runtime: 'node-adapter',
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export default adapter
|