Compare commits

...

11 Commits

Author SHA1 Message Date
whyour f152136f0b 更新版本 v2.13.3 2022-06-08 18:39:51 +08:00
whyour c24d3d6e2f 移除console 2022-06-08 18:39:10 +08:00
whyour 33d820acdb 修改npm peer dependence策略 2022-06-08 11:19:54 +08:00
whyour 78ef75bbfe 移除无用日志 2022-06-08 11:12:15 +08:00
whyour 31541fb87e 修复调试脚本结束条件 2022-06-06 21:25:09 +08:00
whyour 0c169e3f6c 修复调试脚本目录 2022-06-05 21:25:09 +08:00
whyour 73c15cb3bc 修复调试脚本运行需要先保存 2022-06-05 19:26:32 +08:00
whyour 4373a2df49 修复脚本管理和日志管理树字段 2022-06-05 18:44:35 +08:00
whyour cb004cfbf5 修复粘贴订阅私有仓库字段 2022-06-05 11:56:48 +08:00
whyour 7f2d6d159c 修复创建脚本选择目录 2022-06-04 01:26:01 +08:00
whyour 4bba024e4a 新建脚本支持文件上传 2022-06-04 00:12:26 +08:00
27 changed files with 288 additions and 219 deletions
+2 -1
View File
@@ -1 +1,2 @@
sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli
sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli
strict-peer-dependencies=false
-4
View File
@@ -9,10 +9,6 @@ export default defineConfig({
type: 'none',
},
fastRefresh: {},
// antd: {
// dark: true,
// },
mfsu: {},
esbuild: {},
webpack5: {},
dynamicImport: {
-3
View File
@@ -25,7 +25,6 @@ export default (app: Router) => {
}),
});
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -51,7 +50,6 @@ export default (app: Router) => {
}
res.send({ code: 200, data: content });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -76,7 +74,6 @@ export default (app: Router) => {
fs.writeFileSync(path, content);
res.send({ code: 200, message: '保存成功' });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-16
View File
@@ -42,7 +42,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' });
}
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -60,7 +59,6 @@ export default (app: Router) => {
const data = await cronService.run(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -78,7 +76,6 @@ export default (app: Router) => {
const data = await cronService.stop(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -102,7 +99,6 @@ export default (app: Router) => {
);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -123,7 +119,6 @@ export default (app: Router) => {
const data = await cronService.addLabels(req.body.ids, req.body.labels);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -141,7 +136,6 @@ export default (app: Router) => {
const data = await cronService.disabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -159,7 +153,6 @@ export default (app: Router) => {
const data = await cronService.enabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -179,7 +172,6 @@ export default (app: Router) => {
const data = await cronService.log(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -210,7 +202,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' });
}
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -228,7 +219,6 @@ export default (app: Router) => {
const data = await cronService.remove(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -246,7 +236,6 @@ export default (app: Router) => {
const data = await cronService.pin(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -264,7 +253,6 @@ export default (app: Router) => {
const data = await cronService.unPin(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -279,7 +267,6 @@ export default (app: Router) => {
const data = await cronService.import_crontab();
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -299,7 +286,6 @@ export default (app: Router) => {
const data = await cronService.getDb({ id: req.params.id });
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -328,7 +314,6 @@ export default (app: Router) => {
});
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -348,7 +333,6 @@ export default (app: Router) => {
const data = await cronService.logs(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-6
View File
@@ -38,7 +38,6 @@ export default (app: Router) => {
const data = await dependenceService.create(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -61,7 +60,6 @@ export default (app: Router) => {
const data = await dependenceService.update(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -79,7 +77,6 @@ export default (app: Router) => {
const data = await dependenceService.remove(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -97,7 +94,6 @@ export default (app: Router) => {
const data = await dependenceService.remove(req.body, true);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -117,7 +113,6 @@ export default (app: Router) => {
const data = await dependenceService.getDb({ id: req.params.id });
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -135,7 +130,6 @@ export default (app: Router) => {
const data = await dependenceService.reInstall(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-8
View File
@@ -38,7 +38,6 @@ export default (app: Router) => {
const data = await envService.create(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -61,7 +60,6 @@ export default (app: Router) => {
const data = await envService.update(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -79,7 +77,6 @@ export default (app: Router) => {
const data = await envService.remove(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -103,7 +100,6 @@ export default (app: Router) => {
const data = await envService.move(req.params.id, req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -121,7 +117,6 @@ export default (app: Router) => {
const data = await envService.disabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -139,7 +134,6 @@ export default (app: Router) => {
const data = await envService.enabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -160,7 +154,6 @@ export default (app: Router) => {
const data = await envService.updateNames(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -180,7 +173,6 @@ export default (app: Router) => {
const data = await envService.getDb(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-1
View File
@@ -37,7 +37,6 @@ export default (app: Router) => {
const content = getFileContentByName(filePath);
res.send({ code: 200, data: content });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-6
View File
@@ -16,7 +16,6 @@ export default (app: Router) => {
const data = await openService.list();
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -37,7 +36,6 @@ export default (app: Router) => {
const data = await openService.create(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -59,7 +57,6 @@ export default (app: Router) => {
const data = await openService.update(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -77,7 +74,6 @@ export default (app: Router) => {
const data = await openService.remove(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -97,7 +93,6 @@ export default (app: Router) => {
const data = await openService.resetSecret(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -118,7 +113,6 @@ export default (app: Router) => {
const result = await openService.authToken(req.query as any);
return res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
+43 -30
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';
@@ -10,17 +11,38 @@ import { Logger } from 'winston';
import config from '../config';
import * as fs from 'fs';
import { celebrate, Joi } from 'celebrate';
import path, { join } from 'path';
import path, { join, parse } 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) {
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,
@@ -44,7 +66,6 @@ export default (app: Router) => {
const content = getFileContentByName(filePath);
res.send({ code: 200, data: content });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -52,14 +73,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 +83,7 @@ export default (app: Router) => {
content: string;
originFilename: string;
};
if (!path) {
path = config.scriptPath;
}
@@ -84,15 +99,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, '')}`,
@@ -104,7 +122,6 @@ export default (app: Router) => {
fs.writeFileSync(filePath, content);
return res.send({ code: 200 });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -131,7 +148,6 @@ export default (app: Router) => {
fs.writeFileSync(filePath, content);
return res.send({ code: 200 });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -156,7 +172,6 @@ export default (app: Router) => {
fs.unlinkSync(filePath);
res.send({ code: 200 });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -187,7 +202,6 @@ export default (app: Router) => {
return next(err);
});
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -198,22 +212,22 @@ export default (app: Router) => {
celebrate({
body: Joi.object({
filename: Joi.string().required(),
content: Joi.string().optional().allow(''),
path: Joi.string().optional().allow(''),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
try {
let { filename, path } = req.body as {
filename: string;
path: string;
};
const filePath = join(path, filename);
let { filename, content, path } = req.body;
const { name, ext } = parse(filename);
const filePath = join(config.scriptPath, path, `${name}.swap${ext}`);
fs.writeFileSync(filePath, content || '', { encoding: 'utf8' });
const scriptService = Container.get(ScriptService);
const result = await scriptService.runScript(filePath);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -224,22 +238,21 @@ export default (app: Router) => {
celebrate({
body: Joi.object({
filename: Joi.string().required(),
content: Joi.string().optional().allow(''),
path: Joi.string().optional().allow(''),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
try {
let { filename, path } = req.body as {
filename: string;
path: string;
};
const filePath = join(path, filename);
let { filename, content, path } = req.body;
const { name, ext } = parse(filename);
const filePath = join(config.scriptPath, path, `${name}.swap${ext}`);
const scriptService = Container.get(ScriptService);
const result = await scriptService.stopScript(filePath);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-11
View File
@@ -65,7 +65,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' });
}
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -83,7 +82,6 @@ export default (app: Router) => {
const data = await subscriptionService.run(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -101,7 +99,6 @@ export default (app: Router) => {
const data = await subscriptionService.stop(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -119,7 +116,6 @@ export default (app: Router) => {
const data = await subscriptionService.disabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -137,7 +133,6 @@ export default (app: Router) => {
const data = await subscriptionService.enabled(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -157,7 +152,6 @@ export default (app: Router) => {
const data = await subscriptionService.log(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -201,7 +195,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' });
}
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -219,7 +212,6 @@ export default (app: Router) => {
const data = await subscriptionService.remove(req.body);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -239,7 +231,6 @@ export default (app: Router) => {
const data = await subscriptionService.getDb({ id: req.params.id });
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -266,7 +257,6 @@ export default (app: Router) => {
});
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -286,7 +276,6 @@ export default (app: Router) => {
const data = await subscriptionService.logs(req.params.id);
return res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-5
View File
@@ -54,7 +54,6 @@ export default (app: Router) => {
const data = await systemService.getLogRemoveFrequency();
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -76,7 +75,6 @@ export default (app: Router) => {
);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -91,7 +89,6 @@ export default (app: Router) => {
const result = await systemService.checkUpdate();
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -106,7 +103,6 @@ export default (app: Router) => {
const result = await systemService.updateSystem();
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -127,7 +123,6 @@ export default (app: Router) => {
const result = await systemService.notify(req.body);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
-13
View File
@@ -39,7 +39,6 @@ export default (app: Router) => {
const data = await userService.login({ ...req.body }, req);
return res.send(data);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -54,7 +53,6 @@ export default (app: Router) => {
await userService.logout(req.platform);
res.send({ code: 200 });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -75,7 +73,6 @@ export default (app: Router) => {
await userService.updateUsernameAndPassword(req.body);
res.send({ code: 200, message: '更新成功' });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -109,7 +106,6 @@ export default (app: Router) => {
const data = await userService.initTwoFactor();
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -129,7 +125,6 @@ export default (app: Router) => {
const data = await userService.activeTwoFactor(req.body.code);
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -144,7 +139,6 @@ export default (app: Router) => {
const data = await userService.deactiveTwoFactor();
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -166,7 +160,6 @@ export default (app: Router) => {
const data = await userService.twoFactorLogin(req.body, req);
res.send(data);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -181,7 +174,6 @@ export default (app: Router) => {
const data = await userService.getLoginLog();
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -196,7 +188,6 @@ export default (app: Router) => {
const data = await userService.getNotificationMode();
res.send({ code: 200, data });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -211,7 +202,6 @@ export default (app: Router) => {
const result = await userService.updateNotificationMode(req.body);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -232,7 +222,6 @@ export default (app: Router) => {
await userService.updateUsernameAndPassword(req.body);
res.send({ code: 200, message: '更新成功' });
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -247,7 +236,6 @@ export default (app: Router) => {
const result = await userService.updateNotificationMode(req.body);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
@@ -263,7 +251,6 @@ export default (app: Router) => {
const result = await userService.updateAvatar(req.file!.filename);
res.send(result);
} catch (e) {
logger.error('🔥 error: %o', e);
return next(e);
}
},
+2 -2
View File
@@ -9,12 +9,12 @@ import Logger from './loaders/logger';
async function startServer() {
const app = express();
await require('./loaders/db').default();
await require('./loaders/initFile').default();
await require('./loaders/sentry').default({ expressApp: app });
await require('./loaders/db').default();
await require('./loaders/app').default({ expressApp: app });
const server = app
+46 -19
View File
@@ -277,31 +277,58 @@ 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,
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,
type: stats.isDirectory() ? 'directory' : 'file',
key,
parent: relativePath,
};
});
return result;
}
+1 -3
View File
@@ -18,9 +18,7 @@ export default async () => {
await AppModel.sync();
await AuthModel.sync();
await EnvModel.sync();
await SubscriptionModel.sync({ alter: true });
await sequelize.sync();
await SubscriptionModel.sync();
// try {
// const queryInterface = sequelize.getQueryInterface();
+23 -1
View File
@@ -136,7 +136,6 @@ export default ({ app }: { app: Application }) => {
});
app.use(errors());
app.use(Sentry.Handlers.errorHandler());
app.use(
(
@@ -155,6 +154,29 @@ export default ({ app }: { app: Application }) => {
},
);
app.use(
(
err: Error & { errors: any[] },
req: Request,
res: Response,
next: NextFunction,
) => {
if (err.name.includes('Sequelize')) {
return res
.status(500)
.send({
code: 400,
message: `${err.name} ${err.message}`,
validation: err.errors,
})
.end();
}
return next(err);
},
);
app.use(Sentry.Handlers.errorHandler());
app.use(
(
err: Error & { status: number },
+6
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');
+34 -32
View File
@@ -1,8 +1,11 @@
import { Service, Inject } from 'typedi';
import winston from 'winston';
import { spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import SockService from './sock';
import CronService from './cron';
import ScheduleService, { TaskCallbacks } from './schedule';
import config from '../config';
@Service()
export default class ScriptService {
@@ -10,44 +13,43 @@ export default class ScriptService {
@Inject('logger') private logger: winston.Logger,
private sockService: SockService,
private cronService: CronService,
private scheduleService: ScheduleService,
) {}
public async runScript(path: string) {
const cp = spawn(`task -l ${path} now`, { shell: '/bin/bash' });
private taskCallbacks(filePath: string): TaskCallbacks {
return {
onEnd: async (cp, endTime, diff) => {
try {
fs.unlinkSync(filePath);
} catch (error) {}
},
onError: async (message: string) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message,
});
},
onLog: async (message: string) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message,
});
},
};
}
cp.stdout.on('data', (data) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message: data.toString(),
});
});
cp.stderr.on('data', (data) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message: data.toString(),
});
});
cp.on('error', (err) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message: JSON.stringify(err),
});
});
cp.on('close', (err) => {
this.sockService.sendMessage({
type: 'manuallyRunScript',
message: `执行结束`,
});
});
public async runScript(filePath: string) {
const relativePath = path.relative(config.scriptPath, filePath);
const command = `task -l ${relativePath} now`;
this.scheduleService.runTask(command, this.taskCallbacks(filePath));
return { code: 200 };
}
public async stopScript(path: string) {
const err = await this.cronService.killTask(`task -l ${path} now`);
public async stopScript(filePath: string) {
const relativePath = path.relative(config.scriptPath, filePath);
const err = await this.cronService.killTask(`task -l ${relativePath} now`);
const str = err ? `\n${err}` : '';
this.sockService.sendMessage({
type: 'manuallyRunScript',
+3 -7
View File
@@ -31,11 +31,7 @@
"allowedVersions": {
"react": "17",
"react-dom": "17"
},
"ignoreMissing": [
"monaco-editor",
"react-router"
]
}
}
},
"dependencies": {
@@ -63,10 +59,10 @@
"nodemailer": "^6.7.2",
"p-queue": "7.2.0",
"reflect-metadata": "^0.1.13",
"sequelize": "^6.17.0",
"sequelize": "^6.20.1",
"serve-handler": "^6.1.3",
"sockjs": "^0.3.24",
"sqlite3": "npm:@louislam/sqlite3@^15.0.3",
"sqlite3": "npm:@louislam/sqlite3@^15.0.6",
"toad-scheduler": "^1.6.0",
"typedi": "^0.10.0",
"uuid": "^8.3.2",
+1
View File
@@ -88,6 +88,7 @@ check_pm2() {
main() {
echo -e "=====> 开始检测"
npm i -g pnpm
pnpm add -g pm2
copy_dep
check_ql
-1
View File
@@ -305,7 +305,6 @@ patch_version() {
fi
# 兼容pnpm@7
npm i -g pnpm
pnpm setup
source ~/.bashrc
+2 -1
View File
@@ -60,7 +60,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
const getLog = (node: any) => {
request
.get(`${config.apiPrefix}logs/${node.value}?path=${node.parent || ''}`)
.get(`${config.apiPrefix}logs/${node.title}?path=${node.parent || ''}`)
.then((data) => {
setValue(data.data);
});
@@ -113,6 +113,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data}
placeholder="请选择日志"
fieldNames={{ value: 'key', label: 'title' }}
showSearch
onSelect={onSelect}
/>,
+11 -7
View File
@@ -66,7 +66,7 @@ const EditModal = ({
const getDetail = (node: any) => {
request
.get(`${config.apiPrefix}scripts/${node.value}?path=${node.parent || ''}`)
.get(`${config.apiPrefix}scripts/${node.title}?path=${node.parent || ''}`)
.then((data) => {
setValue(data.data);
});
@@ -74,11 +74,13 @@ const EditModal = ({
const run = () => {
setLog('');
const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
request
.put(`${config.apiPrefix}scripts/run`, {
data: {
filename: cNode.value,
filename: cNode.title,
path: cNode.parent || '',
content,
},
})
.then((data) => {
@@ -87,14 +89,16 @@ const EditModal = ({
};
const stop = () => {
if (!cNode || !cNode.value) {
if (!cNode || !cNode.title) {
return;
}
const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
request
.put(`${config.apiPrefix}scripts/stop`, {
data: {
filename: cNode.value,
filename: cNode.title,
path: cNode.parent || '',
content,
},
})
.then((data) => {
@@ -113,8 +117,7 @@ const EditModal = ({
return;
}
if (_message === '执行结束') {
_message = '';
if (_message.includes('执行结束')) {
setTimeout(() => {
setIsRunning(false);
}, 300);
@@ -146,6 +149,7 @@ const EditModal = ({
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData}
placeholder="请选择脚本文件"
fieldNames={{ value: 'key', label: 'title' }}
showSearch
onSelect={onSelect}
/>
@@ -243,7 +247,7 @@ const EditModal = ({
content:
editorRef.current &&
editorRef.current.getValue().replace(/\r\n/g, '\n'),
filename: cNode?.value,
filename: cNode?.title,
}}
/>
<SettingModal
+88 -21
View File
@@ -1,7 +1,17 @@
import React, { useEffect, useState } from 'react';
import { Modal, message, Input, Form, Select } from 'antd';
import {
Modal,
message,
Input,
Form,
Select,
Upload,
Radio,
TreeSelect,
} from 'antd';
import { request } from '@/utils/http';
import config from '@/utils/config';
import { UploadOutlined } from '@ant-design/icons';
const { Option } = Select;
@@ -21,21 +31,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,15 +64,42 @@ const EditScriptNameModal = ({
.finally(() => setLoading(false));
};
const beforeUpload = (file: File) => {
setFile(file);
return false;
};
const typeChange = (e) => {
setType(e.target.value);
};
const getDirs = (data) => {
for (const item of data) {
if (item.children && item.children.length > 0) {
item.children = item.children
.filter((x) => x.type === 'directory')
.map((x) => ({ ...x, disabled: false }));
getDirs(item.children);
}
}
return data;
};
useEffect(() => {
const originDirs = treeData
.filter((x) => x.type === 'directory')
.map((x) => ({ ...x, disabled: false }));
const dirs = getDirs(originDirs);
setDirs(dirs);
}, [treeData]);
useEffect(() => {
form.resetFields();
const originDirs = treeData.filter((x) => x.disabled);
setDirs([{ key: '' }, ...originDirs]);
}, [visible]);
return (
<Modal
title="新建文件"
title="新建脚本"
visible={visible}
forceRender
centered
@@ -73,23 +119,44 @@ 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>
<Form.Item
label="父目录"
name="path"
initialValue={dirs && dirs.length > 0 ? dirs[0].key : ''}
>
<Select placeholder="请选择父目录">
{dirs.map((x) => (
<Option value={x.key}>{x.key || '根'}</Option>
))}
</Select>
{type === 'blank' && (
<Form.Item
name="filename"
label="文件名"
rules={[{ required: true, message: '请输入文件名' }]}
>
<Input placeholder="请输入文件名" />
</Form.Item>
)}
<Form.Item label="父目录" name="path">
<TreeSelect
allowClear
treeData={dirs}
fieldNames={{ value: 'key', label: 'title' }}
placeholder="请选择父目录"
treeDefaultExpandAll
/>
</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>
);
+10 -9
View File
@@ -104,7 +104,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const getDetail = (node: any) => {
request
.get(`${config.apiPrefix}scripts/${node.value}?path=${node.parent || ''}`)
.get(`${config.apiPrefix}scripts/${node.title}?path=${node.parent || ''}`)
.then((data) => {
setValue(data.data);
});
@@ -117,7 +117,6 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const obj = {
node: {
title: s,
value: s,
key: p ? vkey : s,
parent: p,
},
@@ -209,7 +208,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
<>
<Text style={{ wordBreak: 'break-all' }} type="warning">
{currentNode.value}
{currentNode.title}
</Text>{' '}
</>
@@ -222,7 +221,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request
.put(`${config.apiPrefix}scripts`, {
data: {
filename: currentNode.value,
filename: currentNode.title,
path: currentNode.parent || '',
content,
},
@@ -262,7 +261,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request
.delete(`${config.apiPrefix}scripts`, {
data: {
filename: currentNode.value,
filename: currentNode.title,
path: currentNode.parent || '',
},
})
@@ -315,8 +314,9 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
) => {
if (filename) {
const newData = [...data];
const _file = { title: filename, key, value: filename, parent: path };
const _file = { title: filename, key, parent: path };
if (path) {
// TODO: 更新左侧树数据
const parentNodeIndex = newData.findIndex((x) => x.key === path);
if (parentNodeIndex !== -1) {
const parentNode = newData[parentNodeIndex];
@@ -331,7 +331,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
newData.unshift(_file);
}
setData(newData);
onSelect(_file.value, _file);
onSelect(_file.title, _file);
setIsEditing(true);
}
setIsAddFileModalVisible(false);
@@ -341,7 +341,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request
.post(`${config.apiPrefix}scripts/download`, {
data: {
filename: currentNode.value,
filename: currentNode.title,
},
})
.then((_data: any) => {
@@ -349,7 +349,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = currentNode.value;
a.download = currentNode.title;
document.documentElement.appendChild(a);
a.click();
document.documentElement.removeChild(a);
@@ -451,6 +451,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data}
placeholder="请选择脚本"
fieldNames={{ value: 'key', label: 'title' }}
showSearch
onSelect={onSelect}
/>,
+9 -6
View File
@@ -204,13 +204,15 @@ const SubscriptionModal = ({
] = text
.split(' ')
.map((x) => x.trim().replace(/\"/g, '').replace(/\'/, ''));
const _type =
type === 'raw'
? 'file'
: url.startsWith('http')
? 'public-repo'
: 'private-repo';
form.setFieldsValue({
type:
type === 'raw'
? 'file'
: url.startsWith('http')
? 'public-repo'
: 'private-repo',
type: _type,
url,
whitelist,
blacklist,
@@ -219,6 +221,7 @@ const SubscriptionModal = ({
extensions,
alias: formatAlias(url, branch),
});
setType(_type);
}
}, []);
+7 -6
View File
@@ -1,8 +1,9 @@
export const version = '2.13.2';
export const version = '2.13.3';
export const changeLogLink = 'https://t.me/jiao_long/303';
export const changeLog = `2.13.2 版本说明
1. 修复nodejs依赖安装失败pnpm setup
2. 修复订阅禁用失效
3. 修复新建订阅定时验证
4. 修复ssh配置文件更新逻辑
export const changeLog = `2.13.3 版本说明
1. 新建脚本支持文件上传
2. 修复调试脚本运行,不需要每次保存
3. 修复新建脚本选择嵌套目录
4. 修复粘贴订阅,私有仓库字段不更新
5. 修复脚本管理、日志管理树
`;