Files
cli/src/command/deploy.ts
2025-03-09 17:49:10 +08:00

229 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { program as app, Command } from '@/program.ts';
import glob from 'fast-glob';
import path from 'path';
import fs from 'fs';
import FormData from 'form-data';
import { getBaseURL, query } from '@/module/query.ts';
import { getConfig } from '@/module/index.ts';
import inquirer from 'inquirer';
import { packLib, unpackLib } from './publish.ts';
import chalk from 'chalk';
import { installDeps } from '@/uitls/npm.ts';
const command = new Command('deploy')
.description('把前端文件传到服务器')
.argument('<filePath>', 'Path to the file to be uploaded, filepath or directory') // 定义文件路径参数
.option('-v, --version <version>', 'verbose')
.option('-k, --key <key>', 'key')
.option('-y, --yes <yes>', 'yes')
.option('-o, --org <org>', 'org')
.option('-u, --update', 'load current app. set current version in product')
.action(async (filePath, options) => {
try {
let { version, key, yes, update, org } = options;
if (!version || !key) {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'version',
message: 'Enter your version:',
when: () => !version,
},
{
type: 'input',
name: 'key',
message: 'Enter your key:',
when: () => !key,
},
]);
version = answers.version || version;
key = answers.key || key;
}
const pwd = process.cwd();
const directory = path.join(pwd, filePath);
// 获取directory如果是文件夹获取文件夹下所有文件如果是文件获取文件
const stat = fs.statSync(directory);
let _relativeFiles = [];
let isDirectory = false;
if (stat.isDirectory()) {
isDirectory = true;
const gPath = path.join(directory, '**/*');
const files = await glob(gPath, { cwd: pwd, ignore: ['node_modules/**/*'], onlyFiles: true });
_relativeFiles = files.map((file) => path.relative(directory, file));
} else if (stat.isFile()) {
const filename = path.basename(directory);
_relativeFiles = [filename];
}
console.log('upload Files', _relativeFiles);
console.log('upload Files Key', key, version);
if (!yes) {
// 确认是否上传
const confirm = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'Do you want to upload these files?',
},
]);
if (!confirm.confirm) {
return;
}
}
const uploadDirectory = isDirectory ? directory : path.dirname(directory);
const res = await uploadFiles(_relativeFiles, uploadDirectory, { key, version, username: org });
if (res?.code === 200) {
console.log('File uploaded successfully!');
res.data?.data?.files?.map?.((d) => {
console.log('uploaded file', d?.name, d?.path);
});
const { id, data, ...rest } = res.data || {};
if (id && !update) {
console.log(chalk.green('id: '), id);
console.log(chalk.green(`run to load: envision deploy-load ${id}`));
} else if (id && update) {
deployLoadFn(id);
} else {
console.log('rest', JSON.stringify(rest, null, 2));
}
} else {
console.error('File upload failed', res?.message);
}
} catch (error) {
console.error('error', error);
}
});
const uploadFiles = async (
files: string[],
directory: string,
{ key, version, username }: { key: string; version: string; username: string },
): Promise<any> => {
const config = await getConfig();
const form = new FormData();
for (const file of files) {
const filePath = path.join(directory, file);
form.append('file', fs.createReadStream(filePath), {
filename: file,
filepath: file,
});
}
form.append('appKey', key);
form.append('version', version);
if (username) {
form.append('username', username);
}
return new Promise((resolve) => {
const _baseURL = getBaseURL();
const url = new URL(_baseURL);
console.log('upload url', url.hostname, url.protocol, url.port);
form.submit(
{
path: '/api/app/upload',
host: url.hostname,
protocol: url.protocol as any,
port: url.port,
method: 'POST',
headers: {
Authorization: 'Bearer ' + config.token,
...form.getHeaders(),
},
},
(err, res) => {
if (err) {
console.error('Error uploading file:', err.message);
return;
}
// 处理服务器响应
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
try {
const res = JSON.parse(body);
resolve(res);
} catch (e) {
resolve({ code: 500, message: body });
}
});
},
);
});
};
app.addCommand(command);
const deployLoadFn = async (id: string) => {
if (!id) {
console.error(chalk.red('id is required'));
return;
}
const res = await query.post({
path: 'app',
key: 'publish',
data: {
id: id,
},
});
if (res.code === 200) {
console.log('deploy-load success. current version:', res.data?.version);
// /:username/:appName
try {
const { user, key } = res.data;
const baseURL = getBaseURL();
const deployURL = new URL(`/${user}/${key}`, baseURL);
console.log('deployURL', deployURL.href);
} catch (error) {}
} else {
console.error('deploy-load failed', res.message);
}
};
const deployLoad = new Command('deploy-load')
.description('部署加载')
.argument('<id>', 'id')
.action(async (id) => {
deployLoadFn(id);
});
app.addCommand(deployLoad);
const local = new Command('local')
.description('本地部署')
.argument('<key>', 'key')
.option('-i, --ignore', '使用 .npmignore 文件模式去忽略文件进行打包, 不需要package.json中的files字段')
.option('-u, --update', 'query查询 127.0.0.1:11015/api/router?path=local-apps&key=detect')
.action(async (key, opts) => {
console.log('local deploy');
const { outputFilePath } = await packLib(opts?.ignore);
const mainAppPath = getConfig().mainAppPath;
const appsPath = getConfig().appsPath || path.join(mainAppPath, 'apps');
if (!key) {
console.error(chalk.red('key is required'));
return;
}
const appPath = path.join(appsPath, key);
if (!fs.existsSync(appPath)) {
fs.mkdirSync(appPath, { recursive: true });
}
// 复制应用到apps目录下
if (outputFilePath) {
await unpackLib(outputFilePath, appPath);
fs.unlinkSync(outputFilePath);
installDeps({ appPath });
if (opts?.update) {
const res = await query.post(
{ path: 'local-apps', key: 'detect' },
{
url: `http://127.0.0.1:11015/api/router?path=local-apps&key=detect`,
},
);
if (res.code === 200) {
console.log('local deploy success');
} else {
console.error('local deploy failed', res.message);
}
}
}
});
app.addCommand(local);