import { oss } from '@/app.ts'; import { fileIsExist } from '@kevisual/use-config'; import { spawn, spawnSync } from 'child_process'; import { getFileStat, getMinioList, MinioFile } from '@/routes/file/index.ts'; import fs from 'fs'; import path from 'path'; import { appsPath } from '../lib/index.ts'; import { installAppFromKey } from './manager.ts'; import { Readable } from 'stream'; export type InstallAppOpts = { needInstallDeps?: boolean; // minio中 appKey?: string; version?: string; }; /** * 检测路径是否存在 * @param opts * @returns */ export const appPathCheck = async (opts: InstallAppOpts) => { const { appKey } = opts; const directory = path.join(appsPath, appKey); if (fileIsExist(directory)) { return true; } return false; }; export const installApp = async (opts: InstallAppOpts) => { const { needInstallDeps, appKey, version } = opts; const prefix = `${appKey}/${version}`; const pkgPrefix = prefix + '/package.json'; const stat = await getFileStat(pkgPrefix); if (!stat) { throw new Error('App not found'); } const fileList = await getMinioList({ prefix, recursive: true, }); for (const file of fileList) { const { name } = file as MinioFile; const outputPath = path.join(appsPath, appKey, name.replace(prefix, '')); const dir = path.dirname(outputPath); if (!fileIsExist(dir)) { fs.mkdirSync(dir, { recursive: true }); } const fileStream = (await oss.getObject(`${name}`)).Body as Readable; const writeStream = fs.createWriteStream(outputPath); fileStream.pipe(writeStream); await new Promise((resolve, reject) => { writeStream.on('finish', () => resolve(true)); writeStream.on('error', reject); }); } const directory = path.join(appsPath, appKey); if (!fileIsExist(directory)) { fs.mkdirSync(directory, { recursive: true }); } if (needInstallDeps) { try { installDeps({ appPath: directory, isProduction: true, sync: true }); } catch (e) { console.log('installDeps error, [need manual install deps]', e); } } return installAppFromKey(appKey); }; export const checkPnpm = () => { try { spawnSync('pnpm', ['--version']); return true; } catch (e) { return false; } }; type InstallDepsOptions = { appPath: string; isProduction?: boolean; sync?: boolean; }; export const installDeps = async (opts: InstallDepsOptions) => { const { appPath } = opts; const isProduction = opts.isProduction ?? true; const isPnpm = checkPnpm(); const params = ['i']; if (isProduction && isPnpm) { params.push('--production'); } else { params.push('--omit=dev'); } console.log('installDeps', appPath, params); const syncSpawn = opts.sync ? spawnSync : spawn; if (isPnpm) { syncSpawn('pnpm', params, { cwd: appPath, stdio: 'inherit', env: process.env }); } else { syncSpawn('npm', params, { cwd: appPath, stdio: 'inherit', env: process.env }); } };