@cloudbase/app 3.0.0 → 3.0.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.
@@ -1,5 +1,8 @@
1
+ import { EndPointKey } from '@cloudbase/types'
1
2
  import { constants } from '@cloudbase/utilities'
2
3
 
4
+ const ZONE_CHINA = ['ap-shanghai', 'ap-guangzhou', 'ap-shenzhen-fsi', 'ap-shanghai-fsi', 'ap-nanjing', 'ap-beijing', 'ap-chengdu', 'ap-chongqing', 'ap-hongkong']
5
+
3
6
  // @ts-ignore
4
7
  const { setSdkName: setUtilitiesSdkName, setProtocol: setUtilitiesProtocol } = constants
5
8
  /**
@@ -23,15 +26,16 @@ export function getSdkName() {
23
26
  }
24
27
  export const DATA_VERSION = '2020-01-10'
25
28
 
26
- export type EndPointKey = 'CLOUD_API' | 'GATEWAY'
27
-
28
29
  interface EndPointInfo {
29
30
  env: string
30
31
  endPointKey: EndPointKey
31
32
  region?: string
32
33
  baseUrl?: string
33
- protocol?: string
34
+ protocol?: Protocol
34
35
  }
36
+
37
+ type Protocol = 'http' | 'https' | 'http:' | 'https:'
38
+
35
39
  /**
36
40
  * 所有 endPoint 信息
37
41
  * 避免直接操作该数组
@@ -42,18 +46,25 @@ const END_POINT_INFO_ARR: Array<EndPointInfo> = []
42
46
  /** 用来查找 endPoint 的字段 */
43
47
  const END_POINT_INFO_SEARCH_KEYS = ['env', 'endPointKey', 'region']
44
48
 
45
- const DEFAULT_PROTOCOL = 'https:'
49
+ export const DEFAULT_PROTOCOL: 'http:' | 'https:' = 'https:'
46
50
 
47
51
  function findMatchedInfo(info: EndPointInfo) {
52
+ // eslint-disable-next-line max-len, eqeqeq
48
53
  return END_POINT_INFO_ARR.find(targetInfo => END_POINT_INFO_SEARCH_KEYS.filter(searchKey => info[searchKey] != null).every(searchKey => targetInfo[searchKey] === info[searchKey],),)
49
54
  }
50
55
 
51
56
  export function setEndPointInfo(newInfo: EndPointInfo) {
57
+ if (newInfo.protocol && !/:$/.test(newInfo.protocol)) {
58
+ newInfo.protocol = `${newInfo.protocol}:` as Protocol
59
+ }
60
+
52
61
  const endPointInfo = findMatchedInfo(newInfo)
53
62
  if (endPointInfo) {
63
+ // eslint-disable-next-line eqeqeq
54
64
  if (newInfo.baseUrl != null) {
55
65
  endPointInfo.baseUrl = newInfo.baseUrl
56
66
  }
67
+ // eslint-disable-next-line eqeqeq
57
68
  if (newInfo.protocol != null) {
58
69
  endPointInfo.protocol = newInfo.protocol
59
70
  }
@@ -63,7 +74,7 @@ export function setEndPointInfo(newInfo: EndPointInfo) {
63
74
 
64
75
  // 保持旧代码逻辑
65
76
  if (newInfo.endPointKey === 'CLOUD_API') {
66
- setUtilitiesProtocol((newInfo.protocol ?? DEFAULT_PROTOCOL) as 'http' | 'https')
77
+ setUtilitiesProtocol((newInfo.protocol ?? DEFAULT_PROTOCOL) as 'http:' | 'https:')
67
78
  }
68
79
  }
69
80
 
@@ -77,18 +88,23 @@ export interface ISetEndPointWithKey {
77
88
  protocol?: 'http' | 'https'
78
89
  }
79
90
 
80
- export function setGatewayEndPointWithEnv(env: string, protocol?: 'http' | 'https') {
81
- setEndPointInfo({ endPointKey: 'GATEWAY', env, baseUrl: `//${env}.api.tcloudbasegateway.com/v1`, protocol })
91
+ export function setGatewayEndPointWithEnv(env: string, protocol?: Protocol, region = 'ap-shanghai') {
92
+ region = region || 'ap-shanghai'
93
+ let baseUrl = `//${env}.api.tcloudbasegateway.com/v1`
94
+
95
+ if (!ZONE_CHINA.includes(region)) {
96
+ baseUrl = `//${env}.api.intl.tcloudbasegateway.com/v1`
97
+ }
98
+
99
+ setEndPointInfo({ endPointKey: 'GATEWAY', env, baseUrl, protocol })
82
100
  }
83
- export function setRegionLevelEndpoint(env: string, region: string, protocol?: 'http' | 'https') {
84
- const baseUrl = region
85
- ? `//${env}.${region}.tcb-api.tencentcloudapi.com/web`
86
- : `//${env}.ap-shanghai.tcb-api.tencentcloudapi.com/web`
101
+ export function setRegionLevelEndpoint(env: string, region: string, protocol?: Protocol) {
102
+ const baseUrl = `//${env}.${region || 'ap-shanghai'}.tcb-api.tencentcloudapi.com/web`
87
103
  setEndPointInfo({ env, region, baseUrl, protocol, endPointKey: 'CLOUD_API' })
88
104
  }
89
105
 
90
- export function getBaseEndPoint(env: string) {
91
- const info = getEndPointInfo(env, 'CLOUD_API')
106
+ export function getBaseEndPoint(env: string, endPointKey: EndPointKey = 'CLOUD_API') {
107
+ const info = getEndPointInfo(env, endPointKey || 'CLOUD_API')
92
108
 
93
109
  const { protocol: PROTOCOL, baseUrl: BASE_URL } = info
94
110
  const webEndpoint = `${PROTOCOL}${BASE_URL}`
package/src/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import cloudbase from '@cloudbase/app';
2
2
  import { ICloudbase } from '@cloudbase/types';
3
3
  declare global {
4
- interface Window {
5
- cloudbase: ICloudbase;
6
- }
4
+ interface Window {
5
+ cloudbase: ICloudbase;
6
+ }
7
7
  }
8
+ // @ts-ignore
8
9
  export = cloudbase;
9
10
  export default cloudbase;
package/src/index.ts CHANGED
@@ -7,11 +7,12 @@ import {
7
7
  ICloudbaseExtension,
8
8
  KV,
9
9
  ICloudbasePlatformInfo,
10
+ EndPointKey,
11
+ ICloudbaseApis,
10
12
  } from '@cloudbase/types'
11
13
  import { ICloudbaseAuth } from '@cloudbase/types/auth'
12
- import adapterForWxMp from '@cloudbase/adapter-wx_mp'
13
14
  import { registerComponent, registerHook } from './libs/component'
14
- import { Platform } from './libs/adapter'
15
+ import { getWxDefaultAdapter, Platform } from './libs/adapter'
15
16
  import { ICloudbaseComponent, ICloudbaseHook } from '@cloudbase/types/component'
16
17
  import { ICloudbaseCache } from '@cloudbase/types/cache'
17
18
  import { initCache, getCacheByEnvId, getLocalCache } from './libs/cache'
@@ -23,14 +24,13 @@ import {
23
24
  setRegionLevelEndpoint,
24
25
  setSdkName,
25
26
  setGatewayEndPointWithEnv,
26
- type EndPointKey,
27
27
  type ISetEndPointWithKey,
28
28
  setEndPointInfo,
29
29
  getEndPointInfo,
30
30
  getSdkVersion,
31
+ DEFAULT_PROTOCOL,
31
32
  } from './constants/common'
32
33
  import { i18nProxy, LANGS } from './libs/lang'
33
- import { generateApis, generateCallApis } from './libs/callApis'
34
34
  export { getBaseEndPoint } from './constants/common'
35
35
  export { LANGS } from './libs/lang'
36
36
  const { useAdapters, useDefaultAdapter } = adapters
@@ -43,7 +43,7 @@ const { catchErrorsDecorator } = helpers
43
43
  */
44
44
  const DEFAULT_INIT_CONFIG: Partial<ICloudbaseConfig> = {
45
45
  timeout: 15000,
46
- persistence: 'local',
46
+ persistence: 'local', // 持久化存储类型
47
47
  }
48
48
 
49
49
  // timeout上限10分钟
@@ -59,6 +59,8 @@ class Cloudbase implements ICloudbase {
59
59
  public requestClient: any
60
60
  public oauthClient: any
61
61
  public version: string
62
+ public auth: ICloudbase['auth'] & ICloudbaseAuth
63
+ public apis: ICloudbaseApis
62
64
  private cloudbaseConfig: ICloudbaseConfig
63
65
 
64
66
  constructor(config?: ICloudbaseConfig) {
@@ -88,10 +90,6 @@ class Cloudbase implements ICloudbase {
88
90
  return getRequestByEnvId(this.cloudbaseConfig.env)
89
91
  }
90
92
 
91
- get apis(): { [key: string]: typeof generateCallApis } {
92
- return generateApis.call(this)
93
- }
94
-
95
93
  @catchErrorsDecorator({
96
94
  mode: 'sync',
97
95
  title: 'Cloudbase 初始化失败',
@@ -109,6 +107,8 @@ class Cloudbase implements ICloudbase {
109
107
  msg: 'env must not be specified',
110
108
  }),)
111
109
  }
110
+ config.endPointMode = config.endPointMode || 'GATEWAY'
111
+
112
112
  // 初始化时若未兼容平台,则使用默认adapter
113
113
  if (!Platform.adapter) {
114
114
  this.useDefaultAdapter()
@@ -137,12 +137,23 @@ class Cloudbase implements ICloudbase {
137
137
  initCache({ env, persistence, debug, platformInfo: this.platform })
138
138
 
139
139
  setRegionLevelEndpoint(env, config.region || '')
140
- setGatewayEndPointWithEnv(env)
140
+ setGatewayEndPointWithEnv(env, DEFAULT_PROTOCOL, config.region || '')
141
141
 
142
142
  const app = new Cloudbase(this.cloudbaseConfig)
143
- initRequest({ env, region: config.region || '', timeout, oauthClient, _fromApp: app, i18n })
143
+ initRequest({
144
+ env,
145
+ region: config.region || '',
146
+ timeout,
147
+ oauthClient,
148
+ _fromApp: app,
149
+ i18n,
150
+ endPointMode: config.endPointMode,
151
+ })
144
152
  app.requestClient = this.requestClient
145
153
  ;(this as any)?.fire?.('cloudbase_init', app)
154
+
155
+ this.try2InitAuth(app)
156
+
146
157
  return app
147
158
  }
148
159
 
@@ -233,38 +244,11 @@ class Cloudbase implements ICloudbase {
233
244
 
234
245
  // 解析URL参数
235
246
  public parseCaptcha(url) {
236
- let queryObj: any = {}
237
- const matched = url.match(/^(data:.*?)(\?[^#\s]*)?$/)
238
- if (matched) {
239
- url = matched[1]
240
- const search = matched[2]
241
- if (search) {
242
- queryObj = utils.parseQueryString(search)
243
- }
244
- }
245
- const { token, ...restQueryObj } = queryObj
246
- if (/^data:/.test(url) && !token) {
247
- return {
248
- error: 'invalid_argument',
249
- error_description: `invalid captcha data: ${url}`,
250
- }
251
- }
252
- if (!token) {
253
- return {
254
- error: 'unimplemented',
255
- error_description: 'need to impl captcha data',
256
- }
257
- }
258
- // 解析url得到的参数
259
- return {
260
- state: restQueryObj.state,
261
- token, // 验证码token
262
- captchaData: url, // 验证码base64图片
263
- }
247
+ return utils.parseCaptcha(url)
264
248
  }
265
249
 
266
250
  private useDefaultAdapter() {
267
- const { adapter, runtime } = useDefaultAdapter()
251
+ const { adapter, runtime } = useDefaultAdapter.bind(this)()
268
252
  Platform.adapter = adapter as SDKAdapterInterface
269
253
  Platform.runtime = runtime as string
270
254
  }
@@ -281,9 +265,23 @@ class Cloudbase implements ICloudbase {
281
265
  return timeout
282
266
  }
283
267
  }
268
+
269
+ private try2InitAuth(app) {
270
+ try {
271
+ app.auth()
272
+ } catch (error) {
273
+ console.log('try2InitAuth error:', error)
274
+ }
275
+ }
284
276
  }
285
277
 
278
+ // 类型导出
279
+ export type { Cloudbase }
280
+
281
+ // 值导出
286
282
  export const cloudbase: ICloudbase = new Cloudbase()
287
- cloudbase.useAdapters(adapterForWxMp)
288
283
 
284
+ cloudbase.useAdapters(getWxDefaultAdapter())
285
+
286
+ // 默认导出实例
289
287
  export default cloudbase
@@ -1,3 +1,132 @@
1
1
  import { ICloudbasePlatformInfo } from '@cloudbase/types'
2
+ import adapterForWxMp, { WxRequest, WxMpWebSocket, wxMpStorage, parseQueryString } from '@cloudbase/adapter-wx_mp'
3
+ import { IUploadRequestOptions, StorageType } from '@cloudbase/adapter-interface'
4
+
5
+ declare const wx: any
6
+ declare const App: any
7
+ declare const getApp: any
2
8
 
3
9
  export const Platform: ICloudbasePlatformInfo = {}
10
+
11
+ export const getWxDefaultAdapter = () => {
12
+ WxRequest.prototype.upload = function (options: IUploadRequestOptions) {
13
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
14
+ const self = this
15
+ return new Promise((resolve) => {
16
+ const { url, file, data, headers } = options
17
+ const fs = wx.getFileSystemManager() // 读取文件 二进制内容
18
+ const task = wx.request({
19
+ url,
20
+ method: options.method,
21
+ header: {
22
+ 'content-type': ' ', // 小程序 content-type 默认为 application/json, 这里一定要强制为 空, 否则签名错误
23
+ ...headers,
24
+ },
25
+ data: fs.readFileSync(file), // 将二进制文件转为字符串直接赋值到 request payload, 不要以 form 的方式传输
26
+ timeout: this._timeout,
27
+ success(res: { statusCode: number; data: unknown }) {
28
+ const result = {
29
+ statusCode: res.statusCode,
30
+ data: res.data || {},
31
+ }
32
+ if (res.statusCode === 200 && data?.success_action_status) {
33
+ result.statusCode = parseInt(data.success_action_status, 10)
34
+ }
35
+ resolve(result)
36
+ },
37
+ fail(err: unknown) {
38
+ resolve(err)
39
+ },
40
+ complete(err: { errMsg: string }) {
41
+ if (!err?.errMsg) {
42
+ return
43
+ }
44
+ if (!self._timeout || self._restrictedMethods.indexOf('upload') === -1) {
45
+ return
46
+ }
47
+ const { errMsg } = err
48
+ if (errMsg === 'request:fail timeout') {
49
+ console.warn(self._timeoutMsg)
50
+ try {
51
+ task.abort()
52
+ } catch (e) {}
53
+ }
54
+ },
55
+ })
56
+ })
57
+ }
58
+
59
+ function isPlugin() {
60
+ return (
61
+ typeof App === 'undefined'
62
+ && typeof getApp === 'undefined'
63
+ && !wx.onAppHide
64
+ && !wx.offAppHide
65
+ && !wx.onAppShow
66
+ && !wx.offAppShow
67
+ )
68
+ }
69
+ adapterForWxMp.genAdapter = function genAdapter(options) {
70
+ const adapter = {
71
+ root: { globalThis: {} },
72
+ reqClass: WxRequest,
73
+ wsClass: WxMpWebSocket,
74
+ captchaOptions: {
75
+ openURIWithCallback: (_url: string) => {
76
+ // eslint-disable-next-line @typescript-eslint/naming-convention
77
+ const { EventBus } = options
78
+ let queryObj: Record<string, string> = {}
79
+ let url = _url
80
+ console.log('openURIWithCallback', _url)
81
+ const matched = _url.match(/^(data:.*?)(\?[^#\s]*)?$/)
82
+ if (matched) {
83
+ // eslint-disable-next-line prefer-destructuring
84
+ url = matched[1]
85
+ console.log('openURIWithCallback url', url)
86
+ const search = matched[2]
87
+ if (search) {
88
+ queryObj = parseQueryString(search)
89
+ }
90
+ }
91
+ console.log('openURIWithCallback queryObj', queryObj)
92
+ const { token, ...restQueryObj } = queryObj
93
+ if (/^data:/.test(url) && !token) {
94
+ // 如果是 data: 开头的 URL 且没有 token,则直接返回
95
+ return Promise.reject({
96
+ error: 'invalid_argument',
97
+ error_description: `invalie captcha data: ${_url}`,
98
+ })
99
+ }
100
+ if (!token) {
101
+ return Promise.reject({
102
+ error: 'unimplemented',
103
+ error_description: 'need to impl captcha data',
104
+ })
105
+ }
106
+ return new Promise((resolve) => {
107
+ console.log('wait for captcha...')
108
+ EventBus.$emit('CAPTCHA_DATA_CHANGE', { ...restQueryObj, token, url })
109
+
110
+ // 监听事件总线,等待验证码数据变化
111
+ EventBus.$once('RESOLVE_CAPTCHA_DATA', (res: { captcha_token: string; expires_in: number }) => {
112
+ resolve(res)
113
+ })
114
+ })
115
+ },
116
+ },
117
+ localStorage: wxMpStorage,
118
+ primaryStorage: StorageType.local,
119
+ getAppSign() {
120
+ const info = wx.getAccountInfoSync()
121
+ if (isPlugin()) {
122
+ // 插件环境返回插件appid
123
+ return info && info.plugin ? info.plugin.appId : ''
124
+ }
125
+ return info && info.miniProgram ? info.miniProgram.appId : ''
126
+ },
127
+ }
128
+ return adapter
129
+ }
130
+
131
+ return adapterForWxMp
132
+ }
@@ -8,7 +8,8 @@ import {
8
8
  IFetchOptions,
9
9
  } from '@cloudbase/adapter-interface'
10
10
  import { utils, constants, langEvent } from '@cloudbase/utilities'
11
- import { KV } from '@cloudbase/types'
11
+ import { EndPointKey, KV } from '@cloudbase/types'
12
+ import { ICustomReqOpts } from '@cloudbase/types/functions'
12
13
  import {
13
14
  IGetAccessTokenResult,
14
15
  ICloudbaseRequestConfig,
@@ -79,6 +80,14 @@ function beforeEach(): IAppendedRequestInfo {
79
80
  },
80
81
  }
81
82
  }
83
+ export interface IGateWayOptions {
84
+ name?: string
85
+ data?: any
86
+ path?: string
87
+ method: string
88
+ header?: {}
89
+ url?: string
90
+ }
82
91
  export interface ICloudbaseRequest {
83
92
  post: (options: IRequestOptions) => Promise<ResponseObject>
84
93
  upload: (options: IUploadRequestOptions) => Promise<ResponseObject>
@@ -112,21 +121,47 @@ export class CloudbaseRequest implements ICloudbaseRequest {
112
121
  this.reqClass = new Platform.adapter.reqClass(reqConfig)
113
122
  this.throwWhenRequestFail = config.throw || false
114
123
  this.localCache = getLocalCache(this.config.env)
115
- bindHooks(this.reqClass, 'post', [beforeEach])
116
- bindHooks(this.reqClass, 'upload', [beforeEach])
117
- bindHooks(this.reqClass, 'download', [beforeEach])
124
+
125
+ if (this.config.endPointMode !== 'GATEWAY') {
126
+ bindHooks(this.reqClass, 'post', [beforeEach])
127
+ bindHooks(this.reqClass, 'upload', [beforeEach])
128
+ bindHooks(this.reqClass, 'download', [beforeEach])
129
+ }
118
130
 
119
131
  langEvent.bus.on(langEvent.LANG_CHANGE_EVENT, (params) => {
120
132
  this.config.i18n = params.data?.i18n || this.config.i18n
121
133
  })
122
134
  }
123
135
 
136
+ public async getAccessToken(token = null) {
137
+ // eslint-disable-next-line eqeqeq
138
+ if (token != null) {
139
+ return token
140
+ }
141
+ const app = this.config._fromApp
142
+
143
+ if (!app.oauthInstance) {
144
+ throw new Error('you can\'t request without auth')
145
+ }
146
+
147
+ const { oauthInstance } = app
148
+ const oauthClient = oauthInstance.oauth2client
149
+ return (await this.getOauthAccessTokenV2(oauthClient)).accessToken
150
+ }
151
+
124
152
  public getDefaultHeaders() {
125
- return { [this.config.i18n?.LANG_HEADER_KEY]: this.config.i18n?.lang }
153
+ return {
154
+ [this.config.i18n?.LANG_HEADER_KEY]: this.config.i18n?.lang,
155
+ 'X-SDK-Version': `@cloudbase/js-sdk/${getSdkVersion()}`,
156
+ }
126
157
  }
127
158
 
128
- public async post(options: IRequestOptions): Promise<ResponseObject> {
129
- const res = await this.reqClass.post({ ...options, headers: { ...options.headers, ...this.getDefaultHeaders() } })
159
+ public async post(options: IRequestOptions, customReqOpts?: ICustomReqOpts): Promise<ResponseObject> {
160
+ const res = await this.reqClass.post({
161
+ ...options,
162
+ headers: { ...options.headers, ...this.getDefaultHeaders() },
163
+ customReqOpts,
164
+ })
130
165
  return res
131
166
  }
132
167
  public async upload(options: IUploadRequestOptions): Promise<ResponseObject> {
@@ -134,12 +169,15 @@ export class CloudbaseRequest implements ICloudbaseRequest {
134
169
  return res
135
170
  }
136
171
  public async download(options: IRequestOptions): Promise<ResponseObject> {
137
- const res = await this.reqClass.download({ ...options, headers: { ...options.headers, ...this.getDefaultHeaders() } })
172
+ const res = await this.reqClass.download({
173
+ ...options,
174
+ headers: { ...options.headers, ...this.getDefaultHeaders() },
175
+ })
138
176
  return res
139
177
  }
140
178
 
141
- public getBaseEndPoint() {
142
- return getBaseEndPoint(this.config.env)
179
+ public getBaseEndPoint(endPointKey: EndPointKey = 'CLOUD_API') {
180
+ return getBaseEndPoint(this.config.env, endPointKey)
143
181
  }
144
182
 
145
183
  public async getOauthAccessTokenV2(oauthClient: any): Promise<IGetAccessTokenResult> {
@@ -162,7 +200,9 @@ export class CloudbaseRequest implements ICloudbaseRequest {
162
200
  inQuery?: KV<any>
163
201
  search?: string
164
202
  defaultQuery?: KV<any>
203
+ headers?: KV<string>
165
204
  },
205
+ customReqOpts?: ICustomReqOpts,
166
206
  ): Promise<ResponseObject> {
167
207
  const tcbTraceKey = `x-tcb-trace_${this.config.env}`
168
208
  let contentType = 'application/x-www-form-urlencoded'
@@ -209,6 +249,7 @@ export class CloudbaseRequest implements ICloudbaseRequest {
209
249
  headers: {
210
250
  'content-type': contentType,
211
251
  ...this.getDefaultHeaders(),
252
+ ...options?.headers,
212
253
  },
213
254
  }
214
255
  if (options?.onUploadProgress) {
@@ -243,13 +284,25 @@ export class CloudbaseRequest implements ICloudbaseRequest {
243
284
  ...formatQuery,
244
285
  })
245
286
 
246
- const { baseUrl: BASE_URL, protocol: PROTOCOL } = getEndPointInfo(this.config.env, 'CLOUD_API')
287
+ const endPointMode = this.config.endPointMode || 'CLOUD_API'
288
+
289
+ const url = getEndPointInfo(this.config.env, endPointMode)
290
+ let BASE_URL = url.baseUrl
291
+ const PROTOCOL = url.protocol
292
+
293
+ if (endPointMode === 'GATEWAY') {
294
+ if (/^((database)\.)|(auth\.wsWebSign)/.test(action)) {
295
+ const url = getEndPointInfo(this.config.env, 'CLOUD_API')
296
+ BASE_URL = `${url.baseUrl.match(/\/\/([^/?#]*)/)[0]}/web`
297
+ }
298
+ }
299
+
247
300
  // 生成请求 url
248
301
  let newUrl
249
302
  if (options.pathname) {
250
303
  newUrl = formatUrl(
251
304
  PROTOCOL,
252
- `${getBaseEndPoint(this.config.env)?.replace(/^https?:/, '')}/${options.pathname}`,
305
+ `${getBaseEndPoint(this.config.env, endPointMode)?.replace(/^https?:/, '')}/${options.pathname}`,
253
306
  formatQuery,
254
307
  )
255
308
  } else {
@@ -260,11 +313,14 @@ export class CloudbaseRequest implements ICloudbaseRequest {
260
313
  newUrl += search
261
314
  }
262
315
 
263
- const res: ResponseObject = await this.post({
264
- url: newUrl,
265
- data: payload,
266
- ...opts,
267
- })
316
+ const res: ResponseObject = await this.post(
317
+ {
318
+ url: newUrl,
319
+ data: payload,
320
+ ...opts,
321
+ },
322
+ customReqOpts,
323
+ )
268
324
 
269
325
  // 保存 trace header
270
326
  const resTraceHeader = res.header?.['x-tcb-trace']
@@ -279,22 +335,8 @@ export class CloudbaseRequest implements ICloudbaseRequest {
279
335
  return res
280
336
  }
281
337
 
282
- public async fetch(options: IFetchOptions & { token?: string }): Promise<ResponseObject> {
338
+ public async fetch(options: IFetchOptions & { token?: string; customReqOpts?: ICustomReqOpts },): Promise<ResponseObject> {
283
339
  const { token, headers = {}, ...restOptions } = options
284
- const getAccessToken = async () => {
285
- if (token != null) {
286
- return token
287
- }
288
- const app = this.config._fromApp
289
-
290
- if (!app.oauthInstance) {
291
- throw new Error('you can\'t request without auth')
292
- }
293
-
294
- const { oauthInstance } = app
295
- const oauthClient = oauthInstance.oauth2client
296
- return (await this.getOauthAccessTokenV2(oauthClient)).accessToken
297
- }
298
340
 
299
341
  const doFetch = async () => this.reqClass.fetch({
300
342
  headers: {
@@ -302,7 +344,7 @@ export class CloudbaseRequest implements ICloudbaseRequest {
302
344
  // 'X-Request-Id': `${utils.generateRequestId()}`,
303
345
  // 'X-Request-Timestamp': `${Date.now()}`,
304
346
  'X-SDK-Version': `@cloudbase/js-sdk/${getSdkVersion()}`,
305
- Authorization: `Bearer ${await getAccessToken()}`,
347
+ Authorization: `Bearer ${await this.getAccessToken(token)}`,
306
348
  ...this.getDefaultHeaders(),
307
349
  ...headers,
308
350
  },
@@ -327,8 +369,18 @@ export class CloudbaseRequest implements ICloudbaseRequest {
327
369
  }
328
370
  }
329
371
 
330
- public async send(action: string, data: KV<any> = {}, options: KV<any> = {}): Promise<any> {
331
- const response = await this.request(action, data, { ...options, onUploadProgress: data.onUploadProgress })
372
+ public async send(
373
+ action: string,
374
+ data: KV<any> = {},
375
+ options: KV<any> = {},
376
+ customReqOpts?: ICustomReqOpts,
377
+ ): Promise<any> {
378
+ const response = await this.request(
379
+ action,
380
+ data,
381
+ { ...options, onUploadProgress: data.onUploadProgress },
382
+ customReqOpts,
383
+ )
332
384
 
333
385
  if (response.data.code && this.throwWhenRequestFail) {
334
386
  throw new Error(JSON.stringify({
@@ -339,6 +391,62 @@ export class CloudbaseRequest implements ICloudbaseRequest {
339
391
 
340
392
  return response.data
341
393
  }
394
+
395
+ public async gateWay(options: IGateWayOptions, customReqOpts?: ICustomReqOpts) {
396
+ const { url, name, data, path = '', method, header = {} } = options
397
+
398
+ if ((!name || !path) && !url) {
399
+ throw new Error(JSON.stringify({
400
+ code: ERRORS.INVALID_PARAMS,
401
+ msg: '[gateWay] invalid function name or path or url',
402
+ }),)
403
+ }
404
+ let jsonData
405
+ try {
406
+ jsonData = data ? JSON.stringify(data) : ''
407
+ } catch (e) {
408
+ throw new Error(JSON.stringify({
409
+ code: ERRORS.INVALID_PARAMS,
410
+ msg: '[gateWay] invalid data',
411
+ }),)
412
+ }
413
+
414
+ const requestId = utils.generateRequestId()
415
+ const { baseUrl, protocol } = getEndPointInfo(this.config.env, 'GATEWAY')
416
+ let endpoint = `${protocol}${baseUrl}${url || `/${path}/${name}`}`
417
+
418
+ const isGetAndHead = ['GET', 'HEAD'].includes(method?.toUpperCase?.())
419
+
420
+ if (isGetAndHead) {
421
+ try {
422
+ let dataParse = {}
423
+ try {
424
+ dataParse = JSON.parse(data as any)
425
+ } catch (error) {
426
+ dataParse = data || {}
427
+ }
428
+ endpoint = `${endpoint}${endpoint.includes('?') ? '&' : '?'}${Object.keys(dataParse)
429
+ .map(key => `${key}=${dataParse[key]}`)
430
+ .join('&')}`
431
+ } catch (error) {
432
+ //
433
+ }
434
+ }
435
+
436
+ const response = await this.fetch({
437
+ method: method || 'POST',
438
+ headers: {
439
+ 'Content-Type': 'application/json; charset=utf-8',
440
+ 'X-Request-Id': requestId,
441
+ ...header,
442
+ },
443
+ ...(isGetAndHead ? {} : { body: jsonData }),
444
+ url: endpoint,
445
+ customReqOpts,
446
+ })
447
+
448
+ return { requestId, ...response, data: await response.data }
449
+ }
342
450
  }
343
451
 
344
452
  const requestMap: KV<CloudbaseRequest> = {}
@@ -1,9 +0,0 @@
1
- import { ICallApiOptions, KV } from '@cloudbase/types';
2
- import { ResponseObject } from '@cloudbase/adapter-interface';
3
- export declare function callApi(callApiOptions: ICallApiOptions, opts: KV<any>): Promise<ResponseObject['data']>;
4
- export declare function generateCallApis(apiName: string): {
5
- [apiName: string]: typeof callApi;
6
- };
7
- export declare function generateApis(): {
8
- [apiName: string]: typeof generateCallApis;
9
- };