import NodeRSA from 'node-rsa'; import iconv from 'iconv-lite'; import superagent from 'superagent'; import superagentProxy from 'superagent-proxy'; import { forOwn, defaults, isObject, get } from 'lodash'; import { RequestOptions } from './interface'; import { createLogger } from './logger'; import { getConfigByRunEnv } from '../service/config'; const IDE_HOST = process.env.IDE_HOST || 'https://ideservice.alipay.com'; superagentProxy(superagent); const logger = createLogger('http', !!process.env.OPENHOME_HOST); function charset(res, cb) { // TS MODIFY const chunks: any = []; res.on('data', function (chunk) { chunks.push(chunk); }); res.on('end', function () { const contentType = res.headers['content-type'] || ''; const matched = contentType.match(/charset=(.+)/); const charset = matched ? matched[1] : 'utf8'; const buf = Buffer.concat(chunks); const text = iconv.decode(buf, charset); try { res.text = text; res.body = JSON.parse(text); cb(); } catch (e) { cb(e); } }); } function addSign(privateKey, toolId, request, data): object { let key; try { key = new NodeRSA(privateKey, 'pkcs8-private-pem'); } catch (e) { throw new Error('工具私钥配置错误,请重新运行alipaydev config set配置'); } const signKey = new NodeRSA(key.exportKey(), { signingScheme: 'pkcs1-sha256', environment: 'node', }); const params = {} as any; // ASCII code sort const dataStr = Object.keys(data) .sort() .map((item) => { let value = data[item]; if (isObject(value)) { value = JSON.stringify(value); } params[item] = value; return `${item}=${value}`; }) .join('&'); params.toolId = toolId; params.toolType = 'CLI'; const sign = signKey.sign(dataStr).toString('base64'); request.set('signType', 'SHA256WithRSA'); request.set('sign', sign); return params; } export default async function baseRequest(options: RequestOptions): Promise<{ headers: any; body: T; }> { try { const cliConfig = await getConfigByRunEnv(); const { method, path, needSign, beforeSend } = options; const lastHost = cliConfig.endpoint || IDE_HOST; const url = path.startsWith('http') ? path : lastHost + path; const requestProxy = cliConfig.proxy || (url.startsWith('https') ? process.env.HTTPS_PROXY : process.env.HTTP_PROXY); const p = superagent[method.toLowerCase()](url) .set('referer', lastHost) .set('origin', lastHost); const tempData = options.data || {}; // 过滤value为undefined或者null的值 let data = {}; Object.keys(tempData).forEach((item) => { if (tempData[item] !== undefined && tempData[item] !== null) { data[item] = tempData[item]; } }); // 请求需要加签名 if (needSign) { if (!cliConfig.privateKey || !cliConfig.toolId) { return Promise.reject({ code: 'NOT_CONFIG_TOOL_KEY', msg: '工具缺少初始化配置', }); } data = addSign(cliConfig.privateKey, cliConfig.toolId, p, data); } p.buffer(true).parse(charset); if (method === 'GET') { p.query(data); } else if (method === 'POST') { p.type('form'); forOwn(data, function (value, key) { p.field(key, value); }); } logger.info({ prefix: `${method} ${url} request`, message: data, }); if (beforeSend) { beforeSend(p); } if (requestProxy) { p.proxy(requestProxy); } const result = await p; // localhost:9001 logger.verbose({ prefix: `${method} ${url} response`, message: result.body, }); if (result.ok) { return { headers: result.headers, body: result.body, }; } const errorInfo = defaults(result, { code: 'SERVICE_RESPONSE_ERROR', }); const { code } = errorInfo; // 验签失败 if (code === 'SIGN_VERIFY_FAIL') { console.log('验签失败,请检查入参的value是否正常。'); } console.log('请求失败', errorInfo); return Promise.reject(errorInfo); } catch (e) { console.log('请求失败', e); return Promise.reject({ code: 'REQUEST_ERROR', httpStatus: get(e, 'response.body.status'), message: e.message || '请求异常,请稍后再试', }); } }