108 lines
3.1 KiB
TypeScript
108 lines
3.1 KiB
TypeScript
import { oss } from '@/app.ts';
|
|
import { fileIsExist } from '@kevisual/use-config';
|
|
import { spawn, spawnSync } from 'child_process';
|
|
import { 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';
|
|
import { CustomError } from '@kevisual/router';
|
|
export type InstallAppOpts = {
|
|
needInstallDeps?: boolean;
|
|
// minio中
|
|
appKey?: string;
|
|
version?: string;
|
|
uid?: string;
|
|
username?: 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, uid, username } = opts;
|
|
const userAppKey = `${username}/${appKey}`;
|
|
const prefix = `data/${uid}/${appKey}/${version}`;
|
|
const pkgPrefix = prefix + '/package.json';
|
|
const stat = await oss.statObject(pkgPrefix);
|
|
if (!stat) {
|
|
throw new CustomError({ code: 400, message: '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, userAppKey, 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, userAppKey);
|
|
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(userAppKey);
|
|
};
|
|
|
|
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 });
|
|
}
|
|
};
|