cloudbase.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import { Db } from '@cloudbase/database'
  2. import { callFunction } from './functions'
  3. import { auth } from './auth'
  4. import { callWxOpenApi, callCompatibleWxOpenApi, callWxPayApi, wxCallContainerApi } from './wx'
  5. import {
  6. uploadFile,
  7. deleteFile,
  8. getTempFileURL,
  9. downloadFile,
  10. getUploadMetadata,
  11. getFileAuthority
  12. } from './storage'
  13. import {
  14. analytics
  15. } from './analytics'
  16. import {
  17. ICloudBaseConfig,
  18. ICustomReqOpts,
  19. ICustomErrRes,
  20. IDeleteFileRes,
  21. IGetFileUrlRes,
  22. IDownloadFileRes,
  23. IUploadFileRes,
  24. ICallFunctionOptions,
  25. ICallWxOpenApiOptions,
  26. IContextParam,
  27. ICompleteCloudbaseContext,
  28. ISCFContext,
  29. IReportData,
  30. } from './type'
  31. import { DBRequest } from './utils/dbRequest'
  32. import { Log, logger } from './log'
  33. import { ERROR } from './const/code'
  34. import { E } from './utils/utils'
  35. import axios from 'axios'
  36. export class CloudBase {
  37. public static scfContext: ISCFContext
  38. public static parseContext(context: IContextParam): ISCFContext {
  39. if (typeof context !== 'object') {
  40. throw E({ ...ERROR.INVALID_CONTEXT, message: 'context 必须为对象类型' })
  41. }
  42. let {
  43. memory_limit_in_mb,
  44. time_limit_in_ms,
  45. request_id,
  46. environ,
  47. function_version,
  48. namespace,
  49. function_name,
  50. environment
  51. } = context
  52. let parseResult: any = {}
  53. try {
  54. parseResult.memoryLimitInMb = memory_limit_in_mb
  55. parseResult.timeLimitIns = time_limit_in_ms
  56. parseResult.requestId = request_id
  57. parseResult.functionVersion = function_version
  58. parseResult.namespace = namespace
  59. parseResult.functionName = function_name
  60. // 存在environment 为新架构 上新字段 JSON序列化字符串
  61. if (environment) {
  62. parseResult.environment = JSON.parse(environment)
  63. return parseResult
  64. }
  65. // 不存在environment 则为老字段,老架构上存在bug,无法识别value含特殊字符(若允许特殊字符,影响解析,这里特殊处理)
  66. const parseEnviron = environ.split(';')
  67. let parseEnvironObj = {}
  68. for (let i in parseEnviron) {
  69. // value含分号影响切割,未找到= 均忽略
  70. if (parseEnviron[i].indexOf('=') >= 0) {
  71. const equalIndex = parseEnviron[i].indexOf('=')
  72. const key = parseEnviron[i].slice(0, equalIndex)
  73. let value: any = parseEnviron[i].slice(equalIndex + 1)
  74. // value 含, 为数组
  75. if (value.indexOf(',') >= 0) {
  76. value = value.split(',')
  77. }
  78. parseEnvironObj[key] = value
  79. }
  80. }
  81. parseResult.environ = parseEnvironObj
  82. } catch (err) {
  83. throw E({ ...ERROR.INVALID_CONTEXT })
  84. }
  85. CloudBase.scfContext = parseResult
  86. return parseResult
  87. }
  88. /**
  89. * 获取当前函数内的所有环境变量(作为获取变量的统一方法,取值来源process.env 和 context)
  90. */
  91. public static getCloudbaseContext(context?: IContextParam): ICompleteCloudbaseContext {
  92. // WX_CONTEXT_ENV WX_APPID WX_OPENID WX_UNIONID WX_API_TOKEN
  93. // TCB_CONTEXT_ENV TCB_ENV TCB_SEQID TRIGGER_SRC LOGINTYPE QQ_OPENID QQ_APPID TCB_UUID TCB_ISANONYMOUS_USER TCB_SESSIONTOKEN TCB_CUSTOM_USER_ID TCB_SOURCE_IP TCB_SOURCE TCB_ROUTE_KEY TCB_HTTP_CONTEXT TCB_CONTEXT_CNFG
  94. // 解析process.env
  95. const {
  96. TENCENTCLOUD_RUNENV,
  97. SCF_NAMESPACE,
  98. TCB_CONTEXT_KEYS,
  99. TENCENTCLOUD_SECRETID,
  100. TENCENTCLOUD_SECRETKEY,
  101. TENCENTCLOUD_SESSIONTOKEN,
  102. TRIGGER_SRC,
  103. WX_CONTEXT_KEYS,
  104. WX_TRIGGER_API_TOKEN_V0,
  105. WX_CLIENTIP,
  106. WX_CLIENTIPV6,
  107. _SCF_TCB_LOG,
  108. TCB_CONTEXT_CNFG,
  109. LOGINTYPE
  110. } = process.env
  111. let contextEnv: any = {}
  112. if (context) {
  113. const { environment, environ } = CloudBase.parseContext(context)
  114. contextEnv = environment || environ || {}
  115. }
  116. // 从TCB_CONTEXT_KEYS 和 WX_CONTEXT_KEYS中解析环境变量 取值优先级为 context > process.env
  117. const tcb_context_keys = contextEnv.TCB_CONTEXT_KEYS || TCB_CONTEXT_KEYS
  118. const wx_context_keys = contextEnv.WX_CONTEXT_KEYS || WX_CONTEXT_KEYS
  119. let rawContext = {
  120. TENCENTCLOUD_RUNENV,
  121. SCF_NAMESPACE,
  122. TCB_CONTEXT_KEYS,
  123. TENCENTCLOUD_SECRETID,
  124. TENCENTCLOUD_SECRETKEY,
  125. TENCENTCLOUD_SESSIONTOKEN,
  126. TRIGGER_SRC,
  127. WX_TRIGGER_API_TOKEN_V0,
  128. WX_CLIENTIP,
  129. WX_CLIENTIPV6,
  130. WX_CONTEXT_KEYS,
  131. _SCF_TCB_LOG,
  132. TCB_CONTEXT_CNFG,
  133. LOGINTYPE
  134. }
  135. // 遍历keys
  136. if (tcb_context_keys) {
  137. try {
  138. const tcbKeysList = tcb_context_keys.split(',')
  139. for (let item of tcbKeysList) {
  140. rawContext[item] = contextEnv[item] || process.env[item]
  141. }
  142. } catch (e) { }
  143. }
  144. if (wx_context_keys) {
  145. try {
  146. const wxKeysList = wx_context_keys.split(',')
  147. for (let item of wxKeysList) {
  148. rawContext[item] = contextEnv[item] || process.env[item]
  149. }
  150. } catch (e) { }
  151. }
  152. rawContext = { ...rawContext, ...contextEnv }
  153. let finalContext: any = {}
  154. for (let key in rawContext) {
  155. if (rawContext[key] !== undefined) {
  156. finalContext[key] = rawContext[key]
  157. }
  158. }
  159. return finalContext
  160. }
  161. public config: ICloudBaseConfig
  162. private clsLogger: Log
  163. private extensionMap: any
  164. public constructor(config?: ICloudBaseConfig) {
  165. this.init(config)
  166. }
  167. public init(config: ICloudBaseConfig = {}): void {
  168. let {
  169. debug,
  170. secretId,
  171. secretKey,
  172. sessionToken,
  173. env,
  174. timeout,
  175. headers = {},
  176. throwOnCode,
  177. ...restConfig
  178. } = config
  179. if ((secretId && !secretKey) || (!secretId && secretKey)) {
  180. throw E({
  181. ...ERROR.INVALID_PARAM,
  182. message: 'secretId and secretKey must be a pair'
  183. })
  184. }
  185. const newConfig: ICloudBaseConfig = {
  186. ...restConfig,
  187. debug: !!debug,
  188. secretId: secretId,
  189. secretKey: secretKey,
  190. sessionToken: sessionToken,
  191. envName: env,
  192. headers: { ...headers },
  193. timeout: timeout || 15000,
  194. throwOnCode: throwOnCode !== undefined ? throwOnCode : true
  195. }
  196. this.config = newConfig
  197. this.extensionMap = {}
  198. }
  199. public registerExtension(ext: any) {
  200. this.extensionMap[ext.name] = ext
  201. }
  202. public async invokeExtension(name, opts) {
  203. const ext = this.extensionMap[name]
  204. if (!ext) {
  205. throw Error(`扩展${name} 必须先注册`)
  206. }
  207. console.log(opts)
  208. return ext.invoke(opts, this)
  209. }
  210. public database(dbConfig: any = {}): Db {
  211. Db.reqClass = DBRequest
  212. // 兼容方法预处理
  213. if (Object.prototype.toString.call(dbConfig).slice(8, -1) !== 'Object') {
  214. throw E({ ...ERROR.INVALID_PARAM, message: 'dbConfig must be an object' })
  215. }
  216. if (dbConfig && dbConfig.env) {
  217. // env变量名转换
  218. dbConfig.envName = dbConfig.env
  219. delete dbConfig.env
  220. }
  221. return new Db({
  222. ...this.config,
  223. ...dbConfig
  224. })
  225. }
  226. /**
  227. * 调用云函数
  228. *
  229. * @param param0
  230. * @param opts
  231. */
  232. public callFunction(callFunctionOptions: ICallFunctionOptions, opts?: ICustomReqOpts): Promise<any> {
  233. return callFunction(this, callFunctionOptions, opts)
  234. }
  235. public auth(): any {
  236. return auth(this)
  237. }
  238. /**
  239. * openapi调用
  240. *
  241. * @param param0
  242. * @param opts
  243. */
  244. public callWxOpenApi(
  245. wxOpenApiOptions: ICallWxOpenApiOptions,
  246. opts?: ICustomReqOpts
  247. ): Promise<any> {
  248. return callWxOpenApi(this, wxOpenApiOptions, opts)
  249. }
  250. /**
  251. * wxpayapi调用
  252. *
  253. * @param param0
  254. * @param opts
  255. */
  256. public callWxPayApi(
  257. wxOpenApiOptions: ICallWxOpenApiOptions,
  258. opts?: ICustomReqOpts
  259. ): Promise<any> {
  260. return callWxPayApi(this, wxOpenApiOptions, opts)
  261. }
  262. /**
  263. * wxpayapi调用
  264. *
  265. * @param param0
  266. * @param opts
  267. */
  268. public wxCallContainerApi(
  269. wxOpenApiOptions: ICallWxOpenApiOptions,
  270. opts?: ICustomReqOpts
  271. ): Promise<any> {
  272. return wxCallContainerApi(this, wxOpenApiOptions, opts)
  273. }
  274. /**
  275. * 微信云调用
  276. *
  277. * @param param0
  278. * @param opts
  279. */
  280. public callCompatibleWxOpenApi(
  281. wxOpenApiOptions: ICallWxOpenApiOptions,
  282. opts?: ICustomReqOpts
  283. ): Promise<any> {
  284. return callCompatibleWxOpenApi(this, wxOpenApiOptions, opts)
  285. }
  286. /**
  287. * 上传文件
  288. *
  289. * @param param0
  290. * @param opts
  291. */
  292. public uploadFile({ cloudPath, fileContent }, opts?: ICustomReqOpts): Promise<IUploadFileRes> {
  293. return uploadFile(this, { cloudPath, fileContent }, opts)
  294. }
  295. /**
  296. * 删除文件
  297. *
  298. * @param param0
  299. * @param opts
  300. */
  301. public deleteFile(
  302. { fileList },
  303. opts?: ICustomReqOpts
  304. ): Promise<ICustomErrRes | IDeleteFileRes> {
  305. return deleteFile(this, { fileList }, opts)
  306. }
  307. /**
  308. * 获取临时连接
  309. *
  310. * @param param0
  311. * @param opts
  312. */
  313. public getTempFileURL(
  314. { fileList },
  315. opts?: ICustomReqOpts
  316. ): Promise<ICustomErrRes | IGetFileUrlRes> {
  317. return getTempFileURL(this, { fileList }, opts)
  318. }
  319. /**
  320. * 下载文件
  321. *
  322. * @param params
  323. * @param opts
  324. */
  325. public downloadFile(
  326. params: { fileID: string; tempFilePath?: string },
  327. opts?: ICustomReqOpts
  328. ): Promise<ICustomErrRes | IDownloadFileRes> {
  329. return downloadFile(this, params, opts)
  330. }
  331. /**
  332. * 获取上传元数据
  333. *
  334. * @param param0
  335. * @param opts
  336. */
  337. public getUploadMetadata({ cloudPath }, opts?: ICustomReqOpts): Promise<any> {
  338. return getUploadMetadata(this, { cloudPath }, opts)
  339. }
  340. public getFileAuthority({ fileList }, opts?: ICustomReqOpts): Promise<any> {
  341. return getFileAuthority(this, { fileList }, opts)
  342. }
  343. /**
  344. * 获取logger
  345. *
  346. */
  347. public logger(): Log {
  348. if (!this.clsLogger) {
  349. this.clsLogger = logger()
  350. }
  351. return this.clsLogger
  352. }
  353. public analytics(reportData: IReportData): Promise<void> {
  354. return analytics(this, reportData)
  355. }
  356. // shim for tcb extension ci
  357. public get requestClient() {
  358. return {
  359. get: axios,
  360. post: axios,
  361. put: axios,
  362. delete: axios
  363. }
  364. }
  365. }