import request from '../../utils/request'; import fs from 'fs'; import path from 'path'; import { noop } from 'lodash'; import superagent from 'superagent'; import globPack from '../../utils/glob.pack'; import { tmpdir } from '../../utils/util'; export interface DeployCloudFunctionOptions { /** * 小程序appId */ appId: string; /** * 服务空间ID */ spaceId: string; /** * 本地函数根目录 */ project: string; /** * 云函数名 */ name: string; /** * 云函数描述 */ desc?: string; /** * 预览流程回调 */ onProgressUpdate?(info: { /** * LOCAL_PACKAGE: 本地打包成功 * UPLOAD_SUCCESS: 上传代码成功 * DEPLOY_SUCCESS: 部署成功 */ status: 'LOCAL_PACKAGE' | 'UPLOAD_SUCCESS' | 'DEPLOY_SUCCESS'; data: any; }): void; } export interface DeployCloudFunctionResult { /** * 云函数名称 */ name: string; /** * 部署id */ deploymentId: string; } interface CloudFuntionData { /** * 云函数名称 */ name: string; /** * 云函数描述 */ desc: string; /** * 创建时间 */ createdAt: string; /** * 修改时间 */ modifiedAt: string; /** * 云函数超时时间 */ timeout: string; } interface CloudFunctionCreateRes { success: boolean; code: string; message: string; name: string; desc: string; } export interface DeploymentCreateRes { success: boolean; code: string; message: string; deploymentId: string; uploadSignedUrl: string; } interface DeployFunctionRes { success: boolean; code: string; message: string; } async function zipFile(project: string): Promise { const functionName = path.basename(project) + '.zip'; const destDirPath = await tmpdir(); const destPackPath = path.join(destDirPath, functionName); await globPack({ cwd: project, dist: destPackPath, // 云函数不需要上传ts文件 ignore: ['**/*.ts'], }); return destPackPath; } async function deployCloudFunction( options: DeployCloudFunctionOptions ): Promise { const { spaceId, name, project, desc = '', appId } = options; const onProgressUpdate = options.onProgressUpdate || noop; // 0.检查云函数是否存在 const [list, destPackPath] = await Promise.all([ request({ method: 'GET', host: 'ide', path: '/cli/cloud/function/list.json', needSign: true, data: { spaceId, appId, }, }), // 本地打包 zipFile(path.join(project, name)), ]); onProgressUpdate({ status: 'LOCAL_PACKAGE', data: destPackPath, }); const isExist = list.some((value) => name == value.name); if (!isExist) { // 1.创建云函数 const createRes = await request({ method: 'GET', host: 'ide', path: '/cli/cloud/function/createFunction.json', needSign: true, data: { spaceId, name, desc, appId, }, }); if (!createRes.success) { throw new Error(createRes.message); } } // 2.创建deployment const deploymentRes = await request({ method: 'GET', host: 'ide', path: '/cli/cloud/function/createDeployment.json', needSign: true, data: { spaceId, name, appId, }, }); if (!deploymentRes.success) { throw new Error(deploymentRes.message); } const { deploymentId, uploadSignedUrl } = deploymentRes; const ossUrl = uploadSignedUrl.replace(/&/g, '&'); const fsStream = fs.readFileSync(destPackPath); await superagent .put(ossUrl) .send(fsStream) .set('Content-Type', 'application/octet-stream') .set('Accept', 'application/json') .timeout({ // 1分钟超时 deadline: 60000, }) .catch((err) => { if (err.timeout) { throw new Error('云函数上传超时'); } return Promise.reject(err); }); onProgressUpdate({ status: 'UPLOAD_SUCCESS', data: null, }); // 4.部署云函数 const res = await request({ method: 'GET', host: 'ide', path: '/cli/cloud/function/deployFunction.json', needSign: true, data: { spaceId, deployId: deploymentId, appId, }, }); if (!res.success) { throw new Error(res.message); } onProgressUpdate({ status: 'DEPLOY_SUCCESS', data: res, }); return { name, deploymentId, }; } export default deployCloudFunction;