/* eslint-disable import/no-mutable-exports */ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable no-restricted-syntax */ import { ERROR, SECRET_TYPE } from './constants' import { checkIsInScf, getEnv } from './utils' import type { IContextParam, ICompleteCloudbaseContext } from './types' import { ICloudbaseConfig } from '@cloudbase/types' /** * 解析并校验云函数入口的 Context 参数 * 将下划线命名的字段转为驼峰格式,并解析环境变量 * * @param context - 云函数入口传入的 context 对象 * @returns 解析后的上下文对象,包含 environment(新架构)或 environ(老架构) * @throws INVALID_CONTEXT - context 不是对象或解析失败时 */ export function parseContext(context: IContextParam) { if (typeof context !== 'object') { throw { ...ERROR.INVALID_CONTEXT, message: 'context 必须为对象类型' } } const parseResult: any = {} const { memory_limit_in_mb, time_limit_in_ms, request_id, function_version, namespace, function_name, environ, environment, } = context try { parseResult.memoryLimitInMb = memory_limit_in_mb parseResult.timeLimitIns = time_limit_in_ms parseResult.requestId = request_id parseResult.functionVersion = function_version parseResult.namespace = namespace parseResult.functionName = function_name if (environment) { // 新架构:environment 为 JSON 字符串,直接解析 parseResult.environment = JSON.parse(environment) } else { // TODO: 考虑移除老架构的兼容逻辑 // 老架构:environ 为分号分隔的 key=value 字符串 // 存在 bug:value 含特殊字符时可能影响解析 const parseEnviron: any = environ?.split(';') const parseEnvironObj: any = {} // eslint-disable-next-line @typescript-eslint/no-for-in-array for (const i in parseEnviron) { if (parseEnviron[i].includes('=')) { // 只按第一个 = 切割,保留 value 中的 = const equalIndex = parseEnviron[i].indexOf('=') const key = parseEnviron[i].slice(0, equalIndex) let value: any = parseEnviron[i].slice(equalIndex + 1) // value 含逗号时视为数组 if (value.indexOf(',') >= 0) { value = value.split(',') } parseEnvironObj[key] = value } } parseResult.environ = parseEnvironObj } } catch (_err: any) { throw { ...ERROR.INVALID_CONTEXT } } return parseResult } /** * 获取当前云函数内的所有环境变量 * 统一从 process.env 和 context 两个来源合并取值 * * 取值优先级:context > process.env * 通过 TCB_CONTEXT_KEYS / WX_CONTEXT_KEYS 动态扩展需要读取的环境变量列表 * * @param context - 可选,云函数入口的 context 参数 * @returns 合并后的完整环境变量上下文 */ export const getCloudbaseContext = (context?: IContextParam): ICompleteCloudbaseContext => { // 云函数环境下校验必要环境变量 if (checkIsInScf()) { if (!getEnv('TENCENTCLOUD_REGION')) { console.error('[TCB][ERROR] missing `TENCENTCLOUD_REGION` environment') } if (!getEnv('SCF_NAMESPACE')) { console.error('[TCB][ERROR] missing `SCF_NAMESPACE` environment') } } // 第一步:从 process.env 读取基础环境变量 const envObj = getEnv() const envFromProcessEnv: any = { TRIGGER_SRC: envObj.TRIGGER_SRC, _SCF_TCB_LOG: envObj._SCF_TCB_LOG, SCF_NAMESPACE: envObj.SCF_NAMESPACE, TENCENTCLOUD_RUNENV: envObj.TENCENTCLOUD_RUNENV, TENCENTCLOUD_SECRETID: envObj.TENCENTCLOUD_SECRETID, TENCENTCLOUD_SECRETKEY: envObj.TENCENTCLOUD_SECRETKEY, TENCENTCLOUD_SESSIONTOKEN: envObj.TENCENTCLOUD_SESSIONTOKEN, WX_CONTEXT_KEYS: envObj.WX_CONTEXT_KEYS, WX_TRIGGER_API_TOKEN_V0: envObj.WX_TRIGGER_API_TOKEN_V0, WX_CLIENTIP: envObj.WX_CLIENTIP, WX_CLIENTIPV6: envObj.WX_CLIENTIPV6, TCB_CONTEXT_KEYS: envObj.TCB_CONTEXT_KEYS, TCB_CONTEXT_CNFG: envObj.TCB_CONTEXT_CNFG, LOGINTYPE: envObj.LOGINTYPE, } // 第二步:从 context 中解析环境变量 let envFromContext: any = {} if (context) { const { environment, environ } = parseContext(context) envFromContext = environment || environ || {} } // 第三步:根据 CONTEXT_KEYS 动态扩展变量列表(优先取 context 中的值) const tcbContextKeys = envFromContext.TCB_CONTEXT_KEYS || envObj.TCB_CONTEXT_KEYS const wxContextKeys = envFromContext.WX_CONTEXT_KEYS || envObj.WX_CONTEXT_KEYS if (tcbContextKeys) { try { const tcbKeysList = tcbContextKeys.split(',') for (const item of tcbKeysList) { envFromProcessEnv[item] = envFromContext[item] || getEnv(item) } } catch (_e) { // } } if (wxContextKeys) { try { const wxKeysList = wxContextKeys.split(',') for (const item of wxKeysList) { envFromProcessEnv[item] = envFromContext[item] || getEnv(item) } } catch (_e) { // } } // 第四步:合并所有来源,context 的值覆盖 process.env,过滤 undefined const allContext = { ...envFromProcessEnv, ...envFromContext } const finalContext: any = {} for (const key in allContext) { if (allContext[key] !== undefined) { finalContext[key] = allContext[key] } } return finalContext } /** 缓存 extendedContext,供其他模块读取(如 tool.ts 中获取 userId) */ export let INIT_CONFIG: ICloudbaseConfig = {} as unknown as ICloudbaseConfig /** * 从环境变量中获取腾讯云临时密钥信息 * 用于 API 请求签名 */ export const getSecretInfo = (config?: ICloudbaseConfig) => { INIT_CONFIG = JSON.parse(JSON.stringify(config || {})) const { TCB_ENV, SCF_NAMESPACE } = getCloudbaseContext() let env = config?.env || TCB_ENV || SCF_NAMESPACE || '' const defaultInfo = { env, secretId: '', secretKey: '', sessionToken: '', accessKey: '', secretType: '', } if (config?.accessKey) { return defaultInfo } if (config?.auth?.secretId && config?.auth?.secretKey) { return { ...defaultInfo, secretId: config.auth.secretId, secretKey: config.auth.secretKey, secretType: SECRET_TYPE.SECRET, } } const secretEnv = getEnv() const { TENCENTCLOUD_SECRETID } = secretEnv const { TENCENTCLOUD_SECRETKEY } = secretEnv const { TENCENTCLOUD_SESSIONTOKEN } = secretEnv const { CLOUDBASE_APIKEY } = secretEnv if (CLOUDBASE_APIKEY) { return { ...defaultInfo, accessKey: CLOUDBASE_APIKEY, } } if (TENCENTCLOUD_SECRETID && TENCENTCLOUD_SECRETKEY) { return { ...defaultInfo, secretId: TENCENTCLOUD_SECRETID, secretKey: TENCENTCLOUD_SECRETKEY, sessionToken: TENCENTCLOUD_SESSIONTOKEN || '', secretType: SECRET_TYPE.SESSION_SECRET, } } if (config?.context?.extendedContext) { const { extendedContext } = config.context if (!env) { env = extendedContext.envId || '' } return { ...defaultInfo, secretId: extendedContext?.tmpSecret?.secretId || '', secretKey: extendedContext?.tmpSecret?.secretKey || '', sessionToken: extendedContext?.tmpSecret?.token || '', } } return { ...defaultInfo } }