新建脚本支持文件上传

This commit is contained in:
whyour 2022-06-04 00:12:26 +08:00
parent 488fbd2bbb
commit 106b34e33a
4 changed files with 136 additions and 38 deletions

View File

@ -3,6 +3,7 @@ import {
getFileContentByName,
readDirs,
getLastModifyFilePath,
readDir,
} from '../config/util';
import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi';
@ -12,15 +13,37 @@ import * as fs from 'fs';
import { celebrate, Joi } from 'celebrate';
import path, { join } from 'path';
import ScriptService from '../services/script';
import multer from 'multer';
const route = Router();
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, config.scriptPath);
},
filename: function (req, file, cb) {
console.log(req);
cb(null, file.originalname);
},
});
const upload = multer({ storage: storage });
export default (app: Router) => {
app.use('/scripts', route);
route.get('/', async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
try {
const result = readDirs(config.scriptPath, config.scriptPath);
let result = [];
const blacklist = ['node_modules', '.git'];
if (req.query.path) {
const targetPath = path.join(
config.scriptPath,
req.query.path as string,
);
result = readDir(targetPath, config.scriptPath, blacklist);
} else {
result = readDirs(config.scriptPath, config.scriptPath, blacklist);
}
res.send({
code: 200,
data: result,
@ -52,14 +75,7 @@ export default (app: Router) => {
route.post(
'/',
celebrate({
body: Joi.object({
filename: Joi.string().required(),
path: Joi.string().optional().allow(''),
content: Joi.string().allow(''),
originFilename: Joi.string().allow(''),
}),
}),
upload.single('file'),
async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
try {
@ -69,6 +85,7 @@ export default (app: Router) => {
content: string;
originFilename: string;
};
if (!path) {
path = config.scriptPath;
}
@ -84,15 +101,18 @@ export default (app: Router) => {
message: '文件路径禁止访问',
});
}
if (req.file) {
fs.renameSync(req.file.path, join(path, req.file.filename));
return res.send({ code: 200 });
}
if (!originFilename) {
originFilename = filename;
}
const originFilePath = `${path}${originFilename.replace(/\//g, '')}`;
const filePath = `${path}${filename.replace(/\//g, '')}`;
if (fs.existsSync(originFilePath)) {
if (!fs.existsSync(config.bakPath)) {
fs.mkdirSync(config.bakPath);
}
fs.copyFileSync(
originFilePath,
`${config.bakPath}${originFilename.replace(/\//g, '')}`,

View File

@ -277,31 +277,61 @@ export async function concurrentRun(
return replyList;
}
export function readDirs(dir: string, baseDir: string = '') {
export function readDirs(
dir: string,
baseDir: string = '',
blacklist: string[] = [],
) {
const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir);
const result: any = files.map((file: string) => {
const subPath = path.join(dir, file);
const stats = fs.statSync(subPath);
const key = path.join(relativePath, file);
if (stats.isDirectory()) {
const result: any = files
.filter((x) => !blacklist.includes(x))
.map((file: string) => {
const subPath = path.join(dir, file);
const stats = fs.statSync(subPath);
const key = path.join(relativePath, file);
if (stats.isDirectory()) {
return {
title: file,
value: file,
key,
type: 'directory',
disabled: true,
parent: relativePath,
children: readDirs(subPath, baseDir),
};
}
return {
title: file,
value: file,
type: 'file',
key,
type: 'directory',
disabled: true,
parent: relativePath,
children: readDirs(subPath, baseDir),
};
}
return {
title: file,
value: file,
type: 'file',
key,
parent: relativePath,
};
});
});
return result;
}
export function readDir(
dir: string,
baseDir: string = '',
blacklist: string[] = [],
) {
const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir);
const result: any = files
.filter((x) => !blacklist.includes(x))
.map((file: string) => {
const subPath = path.join(dir, file);
const stats = fs.statSync(subPath);
const key = path.join(relativePath, file);
return {
title: file,
value: file,
type: stats.isDirectory() ? 'directory' : 'file',
key,
parent: relativePath,
};
});
return result;
}

View File

@ -11,6 +11,7 @@ const configPath = path.join(dataPath, 'config/');
const scriptPath = path.join(dataPath, 'scripts/');
const logPath = path.join(dataPath, 'log/');
const uploadPath = path.join(dataPath, 'upload/');
const bakPath = path.join(dataPath, 'bak/');
const samplePath = path.join(rootPath, 'sample/');
const confFile = path.join(configPath, 'config.sh');
const authConfigFile = path.join(configPath, 'auth.json');
@ -27,6 +28,7 @@ export default async () => {
const configDirExist = await fileExist(configPath);
const uploadDirExist = await fileExist(uploadPath);
const sshDirExist = await fileExist(sshPath);
const bakDirExist = await fileExist(bakPath);
if (!configDirExist) {
fs.mkdirSync(configPath);
@ -56,6 +58,10 @@ export default async () => {
fs.mkdirSync(sshPath);
}
if (!bakDirExist) {
fs.mkdirSync(bakPath);
}
dotenv.config({ path: confFile });
Logger.info('✌️ Init file down');

View File

@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import { Modal, message, Input, Form, Select } from 'antd';
import { Modal, message, Input, Form, Select, Upload, Radio } from 'antd';
import { request } from '@/utils/http';
import config from '@/utils/config';
import { UploadOutlined } from '@ant-design/icons';
const { Option } = Select;
@ -21,21 +22,30 @@ const EditScriptNameModal = ({
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [dirs, setDirs] = useState<any[]>([]);
const [file, setFile] = useState<File>();
const [type, setType] = useState<'blank' | 'upload'>('blank');
const handleOk = async (values: any) => {
setLoading(true);
values.path = values.path || '';
const formData = new FormData();
formData.append('file', file as any);
formData.append('filename', values.filename);
formData.append('path', values.path);
formData.append('content', '');
request
.post(`${config.apiPrefix}scripts`, {
data: { filename: values.filename, path: values.path, content: '' },
data: formData,
})
.then(({ code, data }) => {
if (code === 200) {
message.success('保存文件成功');
const key = values.path ? `${values.path}-` : '';
const filename = file ? file.name : values.filename;
handleCancel({
filename: values.filename,
filename,
path: values.path,
key: `${values.path}-${values.filename}`,
key: `${key}${filename}`,
});
} else {
message.error(data);
@ -45,6 +55,15 @@ const EditScriptNameModal = ({
.finally(() => setLoading(false));
};
const beforeUpload = (file: File) => {
setFile(file);
return false;
};
const typeChange = (e) => {
setType(e.target.value);
};
useEffect(() => {
form.resetFields();
const originDirs = treeData.filter((x) => x.disabled);
@ -73,12 +92,25 @@ const EditScriptNameModal = ({
>
<Form form={form} layout="vertical" name="edit_name_modal">
<Form.Item
name="filename"
label="文件名"
rules={[{ required: true, message: '请输入文件名' }]}
name="type"
label="类型"
rules={[{ required: true }]}
initialValue={'blank'}
>
<Input placeholder="请输入文件名" />
<Radio.Group onChange={typeChange}>
<Radio value="blank"></Radio>
<Radio value="upload"></Radio>
</Radio.Group>
</Form.Item>
{type === 'blank' && (
<Form.Item
name="filename"
label="文件名"
rules={[{ required: true, message: '请输入文件名' }]}
>
<Input placeholder="请输入文件名" />
</Form.Item>
)}
<Form.Item
label="父目录"
name="path"
@ -90,6 +122,16 @@ const EditScriptNameModal = ({
))}
</Select>
</Form.Item>
{type === 'upload' && (
<Form.Item label="文件" name="file">
<Upload.Dragger beforeUpload={beforeUpload} maxCount={1}>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text"></p>
</Upload.Dragger>
</Form.Item>
)}
</Form>
</Modal>
);