123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- "use strict";
- var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
- };
- var __importStar = (this && this.__importStar) || function (mod) {
- if (mod && mod.__esModule) return mod;
- var result = {};
- if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
- result["default"] = mod;
- return result;
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- const http_1 = __importDefault(require("http"));
- const tracing_1 = require("./tracing");
- const utils = __importStar(require("./utils"));
- const code_1 = require("../const/code");
- const symbol_1 = require("../const/symbol");
- const cloudbase_1 = require("../cloudbase");
- const request_1 = require("./request");
- const requestHook_1 = require("./requestHook");
- const getWxCloudApiToken_1 = require("./getWxCloudApiToken");
- const signature_nodejs_1 = require("@cloudbase/signature-nodejs");
- const url_1 = __importDefault(require("url"));
- // import { version } from '../../package.json'
- const secretManager_1 = __importDefault(require("./secretManager"));
- const { version } = require('../../package.json');
- const { E, second, processReturn, getServerInjectUrl } = utils;
- class Request {
- constructor(args) {
- this.urlPath = '/admin';
- this.defaultTimeout = 15000;
- this.timestamp = new Date().valueOf();
- this.tracingInfo = tracing_1.generateTracingInfo();
- this.slowWarnTimer = null;
- // 请求参数
- this.hooks = {};
- this.args = args;
- this.config = args.config;
- this.opts = args.opts || {};
- this.secretManager = new secretManager_1.default();
- }
- /**
- * 最终发送请求
- */
- async request() {
- // 校验密钥是否存在
- await this.validateSecretIdAndKey();
- // 构造请求参数
- const params = await this.makeParams();
- const opts = await this.makeReqOpts(params);
- const action = this.getAction();
- const key = {
- functions: 'function_name',
- database: 'collectionName',
- wx: 'apiName'
- }[action.split('.')[0]];
- const argopts = this.opts;
- const config = this.config;
- // 发请求时未找到有效环境字段
- if (!params.envName) {
- // 检查config中是否有设置
- if (config.envName) {
- return processReturn(config.throwOnCode, Object.assign({}, code_1.ERROR.INVALID_PARAM, { message: '未取到init 指定 env!' }));
- }
- else {
- console.warn(`当前未指定env,将默认使用第一个创建的环境!`);
- }
- }
- // 注意:必须初始化为 null
- let retryOptions = null;
- if (argopts.retryOptions) {
- retryOptions = argopts.retryOptions;
- }
- else if (config.retries && typeof config.retries === 'number') {
- retryOptions = { retries: config.retries };
- }
- return request_1.extraRequest(opts, {
- debug: config.debug,
- op: `${action}:${this.args.params[key]}@${params.envName}`,
- seqId: this.getSeqId(),
- retryOptions: retryOptions,
- timingsMeasurerOptions: config.timingsMeasurerOptions || {}
- }).then((response) => {
- this.slowWarnTimer && clearTimeout(this.slowWarnTimer);
- const { body } = response;
- if (response.statusCode === 200) {
- let res;
- try {
- res = typeof body === 'string' ? JSON.parse(body) : body;
- if (this.hooks && this.hooks.handleData) {
- res = this.hooks.handleData(res, null, response, body);
- }
- }
- catch (e) {
- res = body;
- }
- return res;
- }
- else {
- const e = E({
- code: response.statusCode,
- message: ` ${response.statusCode} ${http_1.default.STATUS_CODES[response.statusCode]} | [${opts.url}]`
- });
- throw e;
- }
- });
- }
- setHooks(hooks) {
- Object.assign(this.hooks, hooks);
- }
- getSeqId() {
- return this.tracingInfo.seqId;
- }
- /**
- * 接口action
- */
- getAction() {
- const { params } = this.args;
- const { action } = params;
- return action;
- }
- /**
- * 设置超时warning
- */
- setSlowWarning(timeout) {
- const action = this.getAction();
- const { seqId } = this.tracingInfo;
- this.slowWarnTimer = setTimeout(() => {
- /* istanbul ignore next */
- const msg = `Your current request ${action ||
- ''} is longer than 3s, it may be due to the network or your query performance | [${seqId}]`;
- /* istanbul ignore next */
- console.warn(msg);
- }, timeout);
- }
- /**
- * 构造params
- */
- async makeParams() {
- const { TCB_SESSIONTOKEN, TCB_ENV, SCF_NAMESPACE } = cloudbase_1.CloudBase.getCloudbaseContext();
- const args = this.args;
- const opts = this.opts;
- const config = this.config;
- const { eventId } = this.tracingInfo;
- let crossAuthorizationData = opts.getCrossAccountInfo && (await opts.getCrossAccountInfo()).authorization;
- const params = Object.assign({}, args.params, { envName: config.envName, eventId,
- // wxCloudApiToken: process.env.WX_API_TOKEN || '',
- wxCloudApiToken: getWxCloudApiToken_1.getWxCloudApiToken(),
- // 对应服务端 wxCloudSessionToken
- tcb_sessionToken: TCB_SESSIONTOKEN || '', sessionToken: config.sessionToken, sdk_version: version, crossAuthorizationToken: crossAuthorizationData
- ? Buffer.from(JSON.stringify(crossAuthorizationData)).toString('base64')
- : '' });
- // 取当前云函数环境时,替换为云函数下环境变量
- if (params.envName === symbol_1.SYMBOL_CURRENT_ENV) {
- params.envName = TCB_ENV || SCF_NAMESPACE;
- }
- // 过滤value undefined
- utils.filterUndefined(params);
- return params;
- }
- /**
- * 构造请求项
- */
- async makeReqOpts(params) {
- const config = this.config;
- const args = this.args;
- const url = this.getUrl();
- const method = this.getMethod();
- const opts = {
- url: url,
- method,
- // 先取模块的timeout,没有则取sdk的timeout,还没有就使用默认值
- // timeout: args.timeout || config.timeout || 15000,
- timeout: this.getTimeout(),
- // 优先取config,其次取模块,最后取默认
- headers: await this.getHeaders(params),
- proxy: config.proxy
- };
- opts.keepalive = config.keepalive === true;
- if (args.method === 'post') {
- if (args.isFormData) {
- opts.formData = params;
- opts.encoding = null;
- }
- else {
- opts.body = params;
- opts.json = true;
- }
- }
- else {
- /* istanbul ignore next */
- opts.qs = params;
- }
- return opts;
- }
- /**
- * 协议
- */
- getProtocol() {
- return this.config.isHttp === true ? 'http' : 'https';
- }
- /**
- * 请求方法
- */
- getMethod() {
- return this.args.method || 'get';
- }
- /**
- * 超时时间
- */
- getTimeout() {
- const { opts = {} } = this.args;
- // timeout优先级 自定义接口timeout > config配置timeout > 默认timeout
- return opts.timeout || this.config.timeout || this.defaultTimeout;
- }
- /**
- * 校验密钥和token是否存在
- */
- async validateSecretIdAndKey() {
- const { TENCENTCLOUD_SECRETID, TENCENTCLOUD_SECRETKEY, TENCENTCLOUD_SESSIONTOKEN } = cloudbase_1.CloudBase.getCloudbaseContext(); // 放在此处是为了兼容本地环境下读环境变量
- const isInSCF = utils.checkIsInScf();
- const isInContainer = utils.checkIsInContainer();
- let opts = this.opts;
- let getCrossAccountInfo = opts.getCrossAccountInfo || this.config.getCrossAccountInfo;
- /* istanbul ignore if */
- if (getCrossAccountInfo) {
- let crossAccountInfo = await getCrossAccountInfo();
- let { credential } = await getCrossAccountInfo();
- let { secretId, secretKey, token } = credential || {};
- this.config = Object.assign({}, this.config, { secretId,
- secretKey, sessionToken: token });
- this.opts.getCrossAccountInfo = () => Promise.resolve(crossAccountInfo);
- if (!this.config.secretId || !this.config.secretKey) {
- throw E(Object.assign({}, code_1.ERROR.INVALID_PARAM, { message: 'missing secretId or secretKey of tencent cloud' }));
- }
- }
- else {
- const { secretId, secretKey } = this.config;
- if (!secretId || !secretKey) {
- /* istanbul ignore if */
- if (isInContainer) {
- // 这种情况有可能是在容器内,此时尝试拉取临时
- const tmpSecret = await this.secretManager.getTmpSecret();
- this.config = Object.assign({}, this.config, { secretId: tmpSecret.id, secretKey: tmpSecret.key, sessionToken: tmpSecret.token });
- return;
- }
- if (!TENCENTCLOUD_SECRETID || !TENCENTCLOUD_SECRETKEY) {
- if (isInSCF) {
- throw E(Object.assign({}, code_1.ERROR.INVALID_PARAM, { message: 'missing authoration key, redeploy the function' }));
- }
- else {
- throw E(Object.assign({}, code_1.ERROR.INVALID_PARAM, { message: 'missing secretId or secretKey of tencent cloud' }));
- }
- }
- else {
- this.config = Object.assign({}, this.config, { secretId: TENCENTCLOUD_SECRETID, secretKey: TENCENTCLOUD_SECRETKEY, sessionToken: TENCENTCLOUD_SESSIONTOKEN });
- }
- }
- }
- }
- /**
- *
- * 获取headers 此函数中设置authorization
- */
- async getHeaders(params) {
- let { TCB_SOURCE } = cloudbase_1.CloudBase.getCloudbaseContext();
- const config = this.config;
- const { secretId, secretKey } = config;
- const args = this.args;
- const method = this.getMethod();
- const isInSCF = utils.checkIsInScf();
- // Note: 云函数被调用时可能调用端未传递 SOURCE,TCB_SOURCE 可能为空
- TCB_SOURCE = TCB_SOURCE || '';
- const SOURCE = isInSCF ? `${TCB_SOURCE},scf` : ',not_scf';
- const url = this.getUrl();
- // 默认
- let requiredHeaders = {
- 'User-Agent': `tcb-node-sdk/${version}`,
- 'x-tcb-source': SOURCE,
- 'x-client-timestamp': this.timestamp,
- 'X-SDK-Version': `tcb-node-sdk/${version}`,
- Host: url_1.default.parse(url).host
- };
- if (config.version) {
- requiredHeaders['X-SDK-Version'] = config.version;
- }
- if (this.tracingInfo.trace) {
- requiredHeaders['x-tcb-tracelog'] = this.tracingInfo.trace;
- }
- const region = this.config.region || process.env.TENCENTCLOUD_REGION || '';
- if (region) {
- requiredHeaders['X-TCB-Region'] = region;
- }
- requiredHeaders = Object.assign({}, config.headers, args.headers, requiredHeaders);
- const { authorization, timestamp } = signature_nodejs_1.sign({
- secretId: secretId,
- secretKey: secretKey,
- method: method,
- url: url,
- params: await this.makeParams(),
- headers: requiredHeaders,
- withSignedParams: true,
- timestamp: second() - 1
- });
- requiredHeaders['Authorization'] = authorization;
- requiredHeaders['X-Signature-Expires'] = 600;
- requiredHeaders['X-Timestamp'] = timestamp;
- return Object.assign({}, requiredHeaders);
- }
- /**
- * 获取url
- * @param action
- */
- /* eslint-disable-next-line complexity */
- getUrl() {
- const isInSCF = utils.checkIsInScf();
- const isInContainer = utils.checkIsInContainer();
- const { eventId, seqId } = this.tracingInfo;
- const { serviceUrl } = this.config;
- const serverInjectUrl = getServerInjectUrl();
- if (isInSCF) {
- // 云函数环境下,应该包含以下环境变量,如果没有,后续逻辑可能会有问题
- if (!process.env.TENCENTCLOUD_REGION) {
- console.error('[ERROR] missing `TENCENTCLOUD_REGION` environment');
- }
- if (!process.env.SCF_NAMESPACE) {
- console.error('[ERROR] missing `SCF_NAMESPACE` environment');
- }
- }
- const { TCB_ENV, SCF_NAMESPACE } = cloudbase_1.CloudBase.getCloudbaseContext();
- // 优先级:用户配置 > 环境变量
- const region = this.config.region || process.env.TENCENTCLOUD_REGION || '';
- const envId = this.config.envName === symbol_1.SYMBOL_CURRENT_ENV
- ? TCB_ENV || SCF_NAMESPACE
- : this.config.envName || '';
- // 有地域信息则访问地域级别域名,无地域信息则访问默认域名,默认域名固定解析到上海地域保持兼容
- const internetRegionEndpoint = region
- ? `${region}.tcb-api.tencentcloudapi.com`
- : `tcb-api.tencentcloudapi.com`;
- const internalRegionEndpoint = region
- ? `internal.${region}.tcb-api.tencentcloudapi.com`
- : `internal.tcb-api.tencentcloudapi.com`;
- // 同地域走内网,跨地域走公网
- const isSameRegionVisit = this.config.region
- ? this.config.region === process.env.TENCENTCLOUD_REGION
- : true;
- const endpoint = isSameRegionVisit && (isInSCF || isInContainer)
- ? internalRegionEndpoint
- : internetRegionEndpoint;
- const envEndpoint = envId ? `${envId}.${endpoint}` : endpoint;
- const protocol = isInSCF ? 'http' : this.getProtocol();
- // 注意:云函数环境下有地域信息,云应用环境下不确定是否有,如果没有,用户必须显式的传入
- const defaultUrl = `${protocol}://${envEndpoint}${this.urlPath}`;
- const url = serviceUrl || serverInjectUrl || defaultUrl;
- const qs = cloudbase_1.CloudBase.scfContext
- ? `&eventId=${eventId}&seqId=${seqId}&scfRequestId=${cloudbase_1.CloudBase.scfContext.requestId}`
- : `&eventId=${eventId}&seqId=${seqId}`;
- return url.includes('?') ? `${url}${qs}` : `${url}?${qs}`;
- }
- }
- exports.Request = Request;
- // 业务逻辑都放在这里处理
- exports.default = async (args) => {
- const req = new Request(args);
- const config = args.config;
- const { action } = args.params;
- if (action === 'wx.openApi' || action === 'wx.wxPayApi') {
- req.setHooks({ handleData: requestHook_1.handleWxOpenApiData });
- }
- if (action.startsWith('database')) {
- req.setSlowWarning(3000);
- }
- try {
- const res = await req.request();
- // 检查res是否为return {code, message}回包
- if (res && res.code) {
- // 判断是否设置config._returnCodeByThrow = false
- return processReturn(config.throwOnCode, res);
- }
- return res;
- }
- finally {
- //
- }
- };
|