Compare commits

..
38 Commits
Author SHA1 Message Date
whyour c0977f9e37 更新版本 v2.13.5 2022-07-17 22:29:25 +08:00
whyour e9b85347f4 修复shell获取时间 2022-07-17 13:24:59 +08:00
whyour 325482cbb1 更新版本 v2.13.4 2022-07-16 12:31:50 +08:00
whyour 45e02a55c3 修改npm镜像源 2022-07-16 12:25:14 +08:00
whyour 6b8ef75b51 修复获取环境变量详情 2022-07-16 12:16:33 +08:00
whyour f2535ece76 移除内置token判断 2022-07-11 21:21:35 +08:00
whyour b2afa65cb1 任务详情编辑脚本支持全屏 2022-07-04 22:44:51 +08:00
whyour 3a62cc37ff 环境变量增加导出功能 2022-07-04 22:24:50 +08:00
whyour 93c69826ad 脚本管理按文件类型排序 2022-06-26 22:25:21 +08:00
whyour 70727d0e26 修复mac和linux兼容性 2022-06-26 18:59:39 +08:00
whyour 2578aae0ba 修复手动停止任务耗时日志 2022-06-19 14:32:09 +08:00
whyour 616e56ea95 修复系统token验证 2022-06-18 19:24:56 +08:00
whyour 96ae20de68 修复系统token权限 2022-06-18 19:18:14 +08:00
whyour 6975ff4aa1 修复repo命令 2022-06-18 19:03:50 +08:00
whyour 1d8a1e9c4a 修复repo命令 2022-06-18 19:00:22 +08:00
whyour 2719280cdc 修改结束message 2022-06-18 12:45:38 +08:00
whyour 575e41172f 修改日期打印 2022-06-18 12:43:23 +08:00
whyour bd50b1e976 Merge branch 'develop' of github.com:whyour/qinglong into develop 2022-06-18 12:30:11 +08:00
whyour dfb2572b4f 修复node kill进程逻辑 2022-06-18 12:30:02 +08:00
二毛andGitHub 7746b18023 区分原生termux和termux中安装的Linux发行版 (#1498)
检测termux时只看环境变量,以便于区分原生termux和termux中安装的Linux发行版
测试发现${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT}在termux中安装的Linux发行版中仍旧存在,但是发行版中应该不用设置软连接吧
2022-06-14 22:55:49 +08:00
whyour 05c738c7c6 修改系统内部获取token方式 2022-06-14 22:43:18 +08:00
whyour e561e12356 修改更新定时任务参数验证 2022-06-12 21:12:01 +08:00
whyour 595b28ba62 移除notify软链命令 2022-06-10 21:23:43 +08:00
whyour 366caed74e 修复根目录环境变量 2022-06-09 16:44:58 +08:00
whyour 2e26a8c24e 修复shell本地开发兼容性 2022-06-09 12:10:41 +08:00
whyour c09c375066 修复无法更新脚本内容为空 2022-06-08 23:57:22 +08:00
whyour 4f93905507 修改默认pip源 2022-06-08 22:54:44 +08:00
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
43 changed files with 658 additions and 348 deletions
+3 -3
View File
@@ -7,6 +7,6 @@ LOG_LEVEL='debug'
SECRET='whyour' SECRET='whyour'
QINIU_AK = '' QINIU_AK=''
QINIU_SK = '' QINIU_SK=''
QINIU_SCOPE = '' QINIU_SCOPE=''
+2 -1
View File
@@ -1 +1,2 @@
sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli sentrycli_cdnurl=https://npmmirror.com/mirrors/sentry-cli/
strict-peer-dependencies=false
-4
View File
@@ -9,10 +9,6 @@ export default defineConfig({
type: 'none', type: 'none',
}, },
fastRefresh: {}, fastRefresh: {},
// antd: {
// dark: true,
// },
mfsu: {},
esbuild: {}, esbuild: {},
webpack5: {}, webpack5: {},
dynamicImport: { dynamicImport: {
-3
View File
@@ -25,7 +25,6 @@ export default (app: Router) => {
}), }),
}); });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -51,7 +50,6 @@ export default (app: Router) => {
} }
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -76,7 +74,6 @@ export default (app: Router) => {
fs.writeFileSync(path, content); fs.writeFileSync(path, content);
res.send({ code: 200, message: '保存成功' }); res.send({ code: 200, message: '保存成功' });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+7 -23
View File
@@ -42,7 +42,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' }); return res.send({ code: 400, message: 'param schedule error' });
} }
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -60,7 +59,6 @@ export default (app: Router) => {
const data = await cronService.run(req.body); const data = await cronService.run(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -78,7 +76,6 @@ export default (app: Router) => {
const data = await cronService.stop(req.body); const data = await cronService.stop(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -102,7 +99,6 @@ export default (app: Router) => {
); );
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -123,7 +119,6 @@ export default (app: Router) => {
const data = await cronService.addLabels(req.body.ids, req.body.labels); const data = await cronService.addLabels(req.body.ids, req.body.labels);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -141,7 +136,6 @@ export default (app: Router) => {
const data = await cronService.disabled(req.body); const data = await cronService.disabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -159,7 +153,6 @@ export default (app: Router) => {
const data = await cronService.enabled(req.body); const data = await cronService.enabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -179,7 +172,6 @@ export default (app: Router) => {
const data = await cronService.log(req.params.id); const data = await cronService.log(req.params.id);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -190,9 +182,9 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
labels: Joi.array().optional().allow(null), labels: Joi.array().optional().allow(null),
command: Joi.string().optional(), command: Joi.string().required(),
schedule: Joi.string().optional(), schedule: Joi.string().required(),
name: Joi.string().optional(), name: Joi.string().optional().allow(null),
id: Joi.number().required(), id: Joi.number().required(),
}), }),
}), }),
@@ -210,7 +202,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' }); return res.send({ code: 400, message: 'param schedule error' });
} }
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -228,7 +219,6 @@ export default (app: Router) => {
const data = await cronService.remove(req.body); const data = await cronService.remove(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -246,7 +236,6 @@ export default (app: Router) => {
const data = await cronService.pin(req.body); const data = await cronService.pin(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -264,7 +253,6 @@ export default (app: Router) => {
const data = await cronService.unPin(req.body); const data = await cronService.unPin(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -279,7 +267,6 @@ export default (app: Router) => {
const data = await cronService.import_crontab(); const data = await cronService.import_crontab();
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -299,7 +286,6 @@ export default (app: Router) => {
const data = await cronService.getDb({ id: req.params.id }); const data = await cronService.getDb({ id: req.params.id });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -311,10 +297,10 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
ids: Joi.array().items(Joi.number().required()), ids: Joi.array().items(Joi.number().required()),
status: Joi.string().required(), status: Joi.string().required(),
pid: Joi.string().optional(), pid: Joi.string().optional().allow(null),
log_path: Joi.string().optional(), log_path: Joi.string().optional().allow(null),
last_running_time: Joi.number().optional(), last_running_time: Joi.number().optional().allow(null),
last_execution_time: Joi.number().optional(), last_execution_time: Joi.number().optional().allow(null),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
@@ -328,7 +314,6 @@ export default (app: Router) => {
}); });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -348,7 +333,6 @@ export default (app: Router) => {
const data = await cronService.logs(req.params.id); const data = await cronService.logs(req.params.id);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
-6
View File
@@ -38,7 +38,6 @@ export default (app: Router) => {
const data = await dependenceService.create(req.body); const data = await dependenceService.create(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -61,7 +60,6 @@ export default (app: Router) => {
const data = await dependenceService.update(req.body); const data = await dependenceService.update(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -79,7 +77,6 @@ export default (app: Router) => {
const data = await dependenceService.remove(req.body); const data = await dependenceService.remove(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -97,7 +94,6 @@ export default (app: Router) => {
const data = await dependenceService.remove(req.body, true); const data = await dependenceService.remove(req.body, true);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -117,7 +113,6 @@ export default (app: Router) => {
const data = await dependenceService.getDb({ id: req.params.id }); const data = await dependenceService.getDb({ id: req.params.id });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -135,7 +130,6 @@ export default (app: Router) => {
const data = await dependenceService.reInstall(req.body); const data = await dependenceService.reInstall(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+1 -9
View File
@@ -38,7 +38,6 @@ export default (app: Router) => {
const data = await envService.create(req.body); const data = await envService.create(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -61,7 +60,6 @@ export default (app: Router) => {
const data = await envService.update(req.body); const data = await envService.update(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -79,7 +77,6 @@ export default (app: Router) => {
const data = await envService.remove(req.body); const data = await envService.remove(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -103,7 +100,6 @@ export default (app: Router) => {
const data = await envService.move(req.params.id, req.body); const data = await envService.move(req.params.id, req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -121,7 +117,6 @@ export default (app: Router) => {
const data = await envService.disabled(req.body); const data = await envService.disabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -139,7 +134,6 @@ export default (app: Router) => {
const data = await envService.enabled(req.body); const data = await envService.enabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -160,7 +154,6 @@ export default (app: Router) => {
const data = await envService.updateNames(req.body); const data = await envService.updateNames(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -177,10 +170,9 @@ export default (app: Router) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const envService = Container.get(EnvService); const envService = Container.get(EnvService);
const data = await envService.getDb(req.params.id); const data = await envService.getDb({ id: req.params.id });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+5 -2
View File
@@ -6,6 +6,7 @@ import config from '../config';
import { getFileContentByName, readDirs } from '../config/util'; import { getFileContentByName, readDirs } from '../config/util';
import { join } from 'path'; import { join } from 'path';
const route = Router(); const route = Router();
const blacklist = ['.tmp'];
export default (app: Router) => { export default (app: Router) => {
app.use('/logs', route); app.use('/logs', route);
@@ -13,7 +14,7 @@ export default (app: Router) => {
route.get('/', async (req: Request, res: Response, next: NextFunction) => { route.get('/', async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const result = readDirs(config.logPath, config.logPath); const result = readDirs(config.logPath, config.logPath, blacklist);
res.send({ res.send({
code: 200, code: 200,
data: result, data: result,
@@ -29,6 +30,9 @@ export default (app: Router) => {
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
if (blacklist.includes(req.path)) {
return res.send({ code: 403, message: '暂无权限' });
}
const filePath = join( const filePath = join(
config.logPath, config.logPath,
(req.query.path || '') as string, (req.query.path || '') as string,
@@ -37,7 +41,6 @@ export default (app: Router) => {
const content = getFileContentByName(filePath); const content = getFileContentByName(filePath);
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+1 -7
View File
@@ -16,7 +16,6 @@ export default (app: Router) => {
const data = await openService.list(); const data = await openService.list();
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -26,7 +25,7 @@ export default (app: Router) => {
'/apps', '/apps',
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
name: Joi.string().optional().allow(''), name: Joi.string().optional().allow('').disallow('system'),
scopes: Joi.array().items(Joi.string().required()), scopes: Joi.array().items(Joi.string().required()),
}), }),
}), }),
@@ -37,7 +36,6 @@ export default (app: Router) => {
const data = await openService.create(req.body); const data = await openService.create(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -59,7 +57,6 @@ export default (app: Router) => {
const data = await openService.update(req.body); const data = await openService.update(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -77,7 +74,6 @@ export default (app: Router) => {
const data = await openService.remove(req.body); const data = await openService.remove(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -97,7 +93,6 @@ export default (app: Router) => {
const data = await openService.resetSecret(req.params.id); const data = await openService.resetSecret(req.params.id);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -118,7 +113,6 @@ export default (app: Router) => {
const result = await openService.authToken(req.query as any); const result = await openService.authToken(req.query as any);
return res.send(result); return res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+44 -31
View File
@@ -3,6 +3,7 @@ import {
getFileContentByName, getFileContentByName,
readDirs, readDirs,
getLastModifyFilePath, getLastModifyFilePath,
readDir,
} from '../config/util'; } from '../config/util';
import { Router, Request, Response, NextFunction } from 'express'; import { Router, Request, Response, NextFunction } from 'express';
import { Container } from 'typedi'; import { Container } from 'typedi';
@@ -10,17 +11,38 @@ import { Logger } from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
import path, { join } from 'path'; import path, { join, parse } from 'path';
import ScriptService from '../services/script'; import ScriptService from '../services/script';
import multer from 'multer';
const route = Router(); 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) => { export default (app: Router) => {
app.use('/scripts', route); app.use('/scripts', route);
route.get('/', async (req: Request, res: Response, next: NextFunction) => { route.get('/', async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { 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({ res.send({
code: 200, code: 200,
data: result, data: result,
@@ -44,7 +66,6 @@ export default (app: Router) => {
const content = getFileContentByName(filePath); const content = getFileContentByName(filePath);
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -52,14 +73,7 @@ export default (app: Router) => {
route.post( route.post(
'/', '/',
celebrate({ upload.single('file'),
body: Joi.object({
filename: Joi.string().required(),
path: Joi.string().optional().allow(''),
content: Joi.string().allow(''),
originFilename: Joi.string().allow(''),
}),
}),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
@@ -69,6 +83,7 @@ export default (app: Router) => {
content: string; content: string;
originFilename: string; originFilename: string;
}; };
if (!path) { if (!path) {
path = config.scriptPath; path = config.scriptPath;
} }
@@ -84,15 +99,18 @@ export default (app: Router) => {
message: '文件路径禁止访问', message: '文件路径禁止访问',
}); });
} }
if (req.file) {
fs.renameSync(req.file.path, join(path, req.file.filename));
return res.send({ code: 200 });
}
if (!originFilename) { if (!originFilename) {
originFilename = filename; originFilename = filename;
} }
const originFilePath = `${path}${originFilename.replace(/\//g, '')}`; const originFilePath = `${path}${originFilename.replace(/\//g, '')}`;
const filePath = `${path}${filename.replace(/\//g, '')}`; const filePath = `${path}${filename.replace(/\//g, '')}`;
if (fs.existsSync(originFilePath)) { if (fs.existsSync(originFilePath)) {
if (!fs.existsSync(config.bakPath)) {
fs.mkdirSync(config.bakPath);
}
fs.copyFileSync( fs.copyFileSync(
originFilePath, originFilePath,
`${config.bakPath}${originFilename.replace(/\//g, '')}`, `${config.bakPath}${originFilename.replace(/\//g, '')}`,
@@ -104,7 +122,6 @@ export default (app: Router) => {
fs.writeFileSync(filePath, content); fs.writeFileSync(filePath, content);
return res.send({ code: 200 }); return res.send({ code: 200 });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -116,7 +133,7 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
filename: Joi.string().required(), filename: Joi.string().required(),
path: Joi.string().optional().allow(''), path: Joi.string().optional().allow(''),
content: Joi.string().required(), content: Joi.string().required().allow(''),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
@@ -131,7 +148,6 @@ export default (app: Router) => {
fs.writeFileSync(filePath, content); fs.writeFileSync(filePath, content);
return res.send({ code: 200 }); return res.send({ code: 200 });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -156,7 +172,6 @@ export default (app: Router) => {
fs.unlinkSync(filePath); fs.unlinkSync(filePath);
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -187,7 +202,6 @@ export default (app: Router) => {
return next(err); return next(err);
}); });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -198,22 +212,22 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
filename: Joi.string().required(), filename: Joi.string().required(),
content: Joi.string().optional().allow(''),
path: Joi.string().optional().allow(''), path: Joi.string().optional().allow(''),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
let { filename, path } = req.body as { let { filename, content, path } = req.body;
filename: string; const { name, ext } = parse(filename);
path: string; const filePath = join(config.scriptPath, path, `${name}.swap${ext}`);
}; fs.writeFileSync(filePath, content || '', { encoding: 'utf8' });
const filePath = join(path, filename);
const scriptService = Container.get(ScriptService); const scriptService = Container.get(ScriptService);
const result = await scriptService.runScript(filePath); const result = await scriptService.runScript(filePath);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -224,22 +238,21 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
filename: Joi.string().required(), filename: Joi.string().required(),
content: Joi.string().optional().allow(''),
path: Joi.string().optional().allow(''), path: Joi.string().optional().allow(''),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
let { filename, path } = req.body as { let { filename, content, path } = req.body;
filename: string; const { name, ext } = parse(filename);
path: string; const filePath = join(config.scriptPath, path, `${name}.swap${ext}`);
};
const filePath = join(path, filename);
const scriptService = Container.get(ScriptService); const scriptService = Container.get(ScriptService);
const result = await scriptService.stopScript(filePath); const result = await scriptService.stopScript(filePath);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
-11
View File
@@ -65,7 +65,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' }); return res.send({ code: 400, message: 'param schedule error' });
} }
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -83,7 +82,6 @@ export default (app: Router) => {
const data = await subscriptionService.run(req.body); const data = await subscriptionService.run(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -101,7 +99,6 @@ export default (app: Router) => {
const data = await subscriptionService.stop(req.body); const data = await subscriptionService.stop(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -119,7 +116,6 @@ export default (app: Router) => {
const data = await subscriptionService.disabled(req.body); const data = await subscriptionService.disabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -137,7 +133,6 @@ export default (app: Router) => {
const data = await subscriptionService.enabled(req.body); const data = await subscriptionService.enabled(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -157,7 +152,6 @@ export default (app: Router) => {
const data = await subscriptionService.log(req.params.id); const data = await subscriptionService.log(req.params.id);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -201,7 +195,6 @@ export default (app: Router) => {
return res.send({ code: 400, message: 'param schedule error' }); return res.send({ code: 400, message: 'param schedule error' });
} }
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -219,7 +212,6 @@ export default (app: Router) => {
const data = await subscriptionService.remove(req.body); const data = await subscriptionService.remove(req.body);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -239,7 +231,6 @@ export default (app: Router) => {
const data = await subscriptionService.getDb({ id: req.params.id }); const data = await subscriptionService.getDb({ id: req.params.id });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -266,7 +257,6 @@ export default (app: Router) => {
}); });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -286,7 +276,6 @@ export default (app: Router) => {
const data = await subscriptionService.logs(req.params.id); const data = await subscriptionService.logs(req.params.id);
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
-5
View File
@@ -54,7 +54,6 @@ export default (app: Router) => {
const data = await systemService.getLogRemoveFrequency(); const data = await systemService.getLogRemoveFrequency();
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -76,7 +75,6 @@ export default (app: Router) => {
); );
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -91,7 +89,6 @@ export default (app: Router) => {
const result = await systemService.checkUpdate(); const result = await systemService.checkUpdate();
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -106,7 +103,6 @@ export default (app: Router) => {
const result = await systemService.updateSystem(); const result = await systemService.updateSystem();
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -127,7 +123,6 @@ export default (app: Router) => {
const result = await systemService.notify(req.body); const result = await systemService.notify(req.body);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
-13
View File
@@ -39,7 +39,6 @@ export default (app: Router) => {
const data = await userService.login({ ...req.body }, req); const data = await userService.login({ ...req.body }, req);
return res.send(data); return res.send(data);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -54,7 +53,6 @@ export default (app: Router) => {
await userService.logout(req.platform); await userService.logout(req.platform);
res.send({ code: 200 }); res.send({ code: 200 });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -75,7 +73,6 @@ export default (app: Router) => {
await userService.updateUsernameAndPassword(req.body); await userService.updateUsernameAndPassword(req.body);
res.send({ code: 200, message: '更新成功' }); res.send({ code: 200, message: '更新成功' });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -109,7 +106,6 @@ export default (app: Router) => {
const data = await userService.initTwoFactor(); const data = await userService.initTwoFactor();
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -129,7 +125,6 @@ export default (app: Router) => {
const data = await userService.activeTwoFactor(req.body.code); const data = await userService.activeTwoFactor(req.body.code);
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -144,7 +139,6 @@ export default (app: Router) => {
const data = await userService.deactiveTwoFactor(); const data = await userService.deactiveTwoFactor();
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -166,7 +160,6 @@ export default (app: Router) => {
const data = await userService.twoFactorLogin(req.body, req); const data = await userService.twoFactorLogin(req.body, req);
res.send(data); res.send(data);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -181,7 +174,6 @@ export default (app: Router) => {
const data = await userService.getLoginLog(); const data = await userService.getLoginLog();
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -196,7 +188,6 @@ export default (app: Router) => {
const data = await userService.getNotificationMode(); const data = await userService.getNotificationMode();
res.send({ code: 200, data }); res.send({ code: 200, data });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -211,7 +202,6 @@ export default (app: Router) => {
const result = await userService.updateNotificationMode(req.body); const result = await userService.updateNotificationMode(req.body);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -232,7 +222,6 @@ export default (app: Router) => {
await userService.updateUsernameAndPassword(req.body); await userService.updateUsernameAndPassword(req.body);
res.send({ code: 200, message: '更新成功' }); res.send({ code: 200, message: '更新成功' });
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -247,7 +236,6 @@ export default (app: Router) => {
const result = await userService.updateNotificationMode(req.body); const result = await userService.updateNotificationMode(req.body);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
@@ -263,7 +251,6 @@ export default (app: Router) => {
const result = await userService.updateAvatar(req.file!.filename); const result = await userService.updateAvatar(req.file!.filename);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
logger.error('🔥 error: %o', e);
return next(e); return next(e);
} }
}, },
+2 -2
View File
@@ -9,12 +9,12 @@ import Logger from './loaders/logger';
async function startServer() { async function startServer() {
const app = express(); const app = express();
await require('./loaders/db').default();
await require('./loaders/initFile').default(); await require('./loaders/initFile').default();
await require('./loaders/sentry').default({ expressApp: app }); await require('./loaders/sentry').default({ expressApp: app });
await require('./loaders/db').default();
await require('./loaders/app').default({ expressApp: app }); await require('./loaders/app').default({ expressApp: app });
const server = app const server = app
+1
View File
@@ -69,6 +69,7 @@ export default {
'cookie.sh', 'cookie.sh',
'crontab.list', 'crontab.list',
'env.sh', 'env.sh',
'token.json',
], ],
writePathList: [configPath, scriptPath], writePathList: [configPath, scriptPath],
bakPath, bakPath,
+69 -19
View File
@@ -2,6 +2,7 @@ import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import got from 'got'; import got from 'got';
import iconv from 'iconv-lite'; import iconv from 'iconv-lite';
import { exec } from 'child_process';
export function getFileContentByName(fileName: string) { export function getFileContentByName(fileName: string) {
if (fs.existsSync(fileName)) { if (fs.existsSync(fileName)) {
@@ -277,31 +278,80 @@ export async function concurrentRun(
return replyList; return replyList;
} }
export function readDirs(dir: string, baseDir: string = '') { enum FileType {
'directory',
'file',
}
export function readDirs(
dir: string,
baseDir: string = '',
blacklist: string[] = [],
) {
const relativePath = path.relative(baseDir, dir); const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir); const files = fs.readdirSync(dir);
const result: any = files.map((file: string) => { const result: any = files
const subPath = path.join(dir, file); .filter((x) => !blacklist.includes(x))
const stats = fs.statSync(subPath); .map((file: string) => {
const key = path.join(relativePath, file); const subPath = path.join(dir, file);
if (stats.isDirectory()) { 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).sort(
(a: any, b: any) =>
(FileType as any)[a.type] - (FileType as any)[b.type],
),
};
}
return { return {
title: file, title: file,
value: file, type: 'file',
key, key,
type: 'directory',
disabled: true,
parent: relativePath, parent: relativePath,
children: readDirs(subPath, baseDir),
}; };
} });
return { return result.sort(
title: file, (a: any, b: any) => (FileType as any)[a.type] - (FileType as any)[b.type],
value: file, );
type: 'file', }
key,
parent: relativePath, 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; return result;
} }
export function promiseExec(command: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(
command,
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' },
(err, stdout, stderr) => {
resolve(stdout || stderr || JSON.stringify(err));
},
);
});
}
+1 -3
View File
@@ -18,9 +18,7 @@ export default async () => {
await AppModel.sync(); await AppModel.sync();
await AuthModel.sync(); await AuthModel.sync();
await EnvModel.sync(); await EnvModel.sync();
await SubscriptionModel.sync({ alter: true }); await SubscriptionModel.sync();
await sequelize.sync();
// try { // try {
// const queryInterface = sequelize.getQueryInterface(); // const queryInterface = sequelize.getQueryInterface();
+26
View File
@@ -2,6 +2,7 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import chokidar from 'chokidar'; import chokidar from 'chokidar';
import config from '../config/index'; import config from '../config/index';
import { promiseExec } from '../config/util';
function linkToNodeModule(src: string, dst?: string) { function linkToNodeModule(src: string, dst?: string) {
const target = path.join(config.rootPath, 'node_modules', dst || src); const target = path.join(config.rootPath, 'node_modules', dst || src);
@@ -16,7 +17,32 @@ function linkToNodeModule(src: string, dst?: string) {
}); });
} }
async function linkCommand() {
const commandPath = await promiseExec('which node');
const commandDir = path.dirname(commandPath);
const linkShell = [
{
src: 'update.sh',
dest: 'ql',
},
{
src: 'task.sh',
dest: 'task',
},
];
for (const link of linkShell) {
const source = path.join(config.rootPath, 'shell', link.src);
const target = path.join(commandDir, link.dest);
if (fs.existsSync(target)) {
fs.unlinkSync(target);
}
fs.symlink(source, target, (err) => {});
}
}
export default async (src: string = 'deps') => { export default async (src: string = 'deps') => {
await linkCommand();
linkToNodeModule(src); linkToNodeModule(src);
const source = path.join(config.rootPath, src); const source = path.join(config.rootPath, src);
+23 -8
View File
@@ -80,13 +80,6 @@ export default ({ app }: { app: Application }) => {
) { ) {
return next(); return next();
} }
const remoteAddress = req.socket.remoteAddress;
if (
remoteAddress === '::ffff:127.0.0.1' &&
originPath === '/api/crons/status'
) {
return next();
}
const data = fs.readFileSync(config.authConfigFile, 'utf8'); const data = fs.readFileSync(config.authConfigFile, 'utf8');
if (data) { if (data) {
@@ -136,7 +129,6 @@ export default ({ app }: { app: Application }) => {
}); });
app.use(errors()); app.use(errors());
app.use(Sentry.Handlers.errorHandler());
app.use( app.use(
( (
@@ -155,6 +147,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( app.use(
( (
err: Error & { status: number }, err: Error & { status: number },
+14
View File
@@ -11,6 +11,7 @@ const configPath = path.join(dataPath, 'config/');
const scriptPath = path.join(dataPath, 'scripts/'); const scriptPath = path.join(dataPath, 'scripts/');
const logPath = path.join(dataPath, 'log/'); const logPath = path.join(dataPath, 'log/');
const uploadPath = path.join(dataPath, 'upload/'); const uploadPath = path.join(dataPath, 'upload/');
const bakPath = path.join(dataPath, 'bak/');
const samplePath = path.join(rootPath, 'sample/'); const samplePath = path.join(rootPath, 'sample/');
const confFile = path.join(configPath, 'config.sh'); const confFile = path.join(configPath, 'config.sh');
const authConfigFile = path.join(configPath, 'auth.json'); const authConfigFile = path.join(configPath, 'auth.json');
@@ -27,6 +28,7 @@ export default async () => {
const configDirExist = await fileExist(configPath); const configDirExist = await fileExist(configPath);
const uploadDirExist = await fileExist(uploadPath); const uploadDirExist = await fileExist(uploadPath);
const sshDirExist = await fileExist(sshPath); const sshDirExist = await fileExist(sshPath);
const bakDirExist = await fileExist(bakPath);
if (!configDirExist) { if (!configDirExist) {
fs.mkdirSync(configPath); fs.mkdirSync(configPath);
@@ -56,7 +58,19 @@ export default async () => {
fs.mkdirSync(sshPath); fs.mkdirSync(sshPath);
} }
if (!bakDirExist) {
fs.mkdirSync(bakPath);
}
dotenv.config({ path: confFile }); dotenv.config({ path: confFile });
// 声明QL_DIR环境变量
let qlHomePath = path.join(__dirname, '../../');
// 生产环境
if (qlHomePath.endsWith('/static/')) {
qlHomePath = path.join(qlHomePath, '../');
}
process.env.QL_DIR = qlHomePath;
Logger.info('✌️ Init file down'); Logger.info('✌️ Init file down');
}; };
+13 -3
View File
@@ -10,6 +10,7 @@ import { promises, existsSync } from 'fs';
import { promisify } from 'util'; import { promisify } from 'util';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
import path from 'path'; import path from 'path';
import dayjs from 'dayjs';
@Service() @Service()
export default class CronService { export default class CronService {
@@ -197,13 +198,21 @@ export default class CronService {
const err = await this.killTask(doc.command); const err = await this.killTask(doc.command);
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`); const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
const logFileExist = doc.log_path && (await fileExist(absolutePath)); const logFileExist = doc.log_path && (await fileExist(absolutePath));
const endTime = dayjs();
const diffTimeStr = doc.last_execution_time
? `,耗时 ${endTime.diff(
dayjs(doc.last_execution_time * 1000),
'second',
)}`
: '';
if (logFileExist) { if (logFileExist) {
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';
fs.appendFileSync( fs.appendFileSync(
`${absolutePath}`, `${absolutePath}`,
`${str}\n## 执行结束... ${new Date() `${str}\n## 执行结束... ${endTime.format(
.toLocaleString('zh', { hour12: false }) 'YYYY-MM-DD HH:mm:ss',
.replace(' 24:', ' 00:')} `, )}${diffTimeStr}`,
); );
} }
} }
@@ -228,6 +237,7 @@ export default class CronService {
const killLogs = []; const killLogs = [];
if (pids && pids.length > 0) { if (pids && pids.length > 0) {
// node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个 // node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个
pids = pids.slice(0, 3);
for (const id of pids) { for (const id of pids) {
const c = `kill -9 ${id.slice(1)}`; const c = `kill -9 ${id.slice(1)}`;
try { try {
+14 -20
View File
@@ -14,6 +14,7 @@ import { spawn } from 'child_process';
import SockService from './sock'; import SockService from './sock';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
import { concurrentRun } from '../config/util'; import { concurrentRun } from '../config/util';
import dayjs from 'dayjs';
@Service() @Service()
export default class DependenceService { export default class DependenceService {
@@ -161,20 +162,17 @@ export default class DependenceService {
)[dependencies[0].type as any]; )[dependencies[0].type as any];
const actionText = isInstall ? '安装' : '删除'; const actionText = isInstall ? '安装' : '删除';
const depIds = dependencies.map((x) => x.id) as number[]; const depIds = dependencies.map((x) => x.id) as number[];
const startTime = Date.now(); const startTime = dayjs();
const message = `开始${actionText}依赖 ${depNames},开始时间 ${startTime.format(
'YYYY-MM-DD HH:mm:ss',
)}\n\n`;
this.sockService.sendMessage({ this.sockService.sendMessage({
type: socketMessageType, type: socketMessageType,
message: `开始${actionText}依赖 ${depNames},开始时间 ${new Date( message,
startTime,
).toLocaleString()}\n\n`,
references: depIds, references: depIds,
}); });
await this.updateLog( await this.updateLog(depIds, message);
depIds,
`开始${actionText}依赖 ${depNames},开始时间 ${new Date(
startTime,
).toLocaleString()}\n\n`,
);
const cp = spawn(`${depRunCommand} ${depNames}`, { shell: '/bin/bash' }); const cp = spawn(`${depRunCommand} ${depNames}`, { shell: '/bin/bash' });
@@ -207,23 +205,19 @@ export default class DependenceService {
}); });
cp.on('close', async (code) => { cp.on('close', async (code) => {
const endTime = Date.now(); const endTime = dayjs();
const isSucceed = code === 0; const isSucceed = code === 0;
const resultText = isSucceed ? '成功' : '失败'; const resultText = isSucceed ? '成功' : '失败';
const message = `\n依赖${actionText}${resultText},结束时间 ${endTime.format(
'YYYY-MM-DD HH:mm:ss',
)},耗时 ${endTime.diff(startTime, 'second')}`;
this.sockService.sendMessage({ this.sockService.sendMessage({
type: socketMessageType, type: socketMessageType,
message: `\n依赖${actionText}${resultText},结束时间 ${new Date( message,
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
references: depIds, references: depIds,
}); });
await this.updateLog( await this.updateLog(depIds, message);
depIds,
`\n依赖${actionText}${resultText},结束时间 ${new Date(
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
);
let status = null; let status = null;
if (isSucceed) { if (isSucceed) {
+34 -1
View File
@@ -90,7 +90,9 @@ export default class OpenService {
} }
try { try {
const result = await this.find(condition); const result = await this.find(condition);
return result.map((x) => ({ ...x, tokens: [] })); return result
.filter((x) => x.name !== 'system')
.map((x) => ({ ...x, tokens: [] }));
} catch (error) { } catch (error) {
throw error; throw error;
} }
@@ -142,4 +144,35 @@ export default class OpenService {
return { code: 400, message: 'client_id或client_seret有误' }; return { code: 400, message: 'client_id或client_seret有误' };
} }
} }
public async findSystemToken(): Promise<{
value: string;
expiration: number;
}> {
let systemApp = (await AppModel.findOne({
where: { name: 'system' },
})) as App;
if (!systemApp) {
systemApp = await this.create({
name: 'system',
scopes: ['crons', 'system'],
} as App);
}
const nowTime = Math.round(Date.now() / 1000);
let token;
if (
!systemApp.tokens ||
!systemApp.tokens.length ||
nowTime > [...systemApp.tokens].pop()!.expiration
) {
const authToken = await this.authToken({
client_id: systemApp.client_id,
client_secret: systemApp.client_secret,
});
token = authToken.data;
} else {
token = [...systemApp.tokens].pop();
}
return token;
}
} }
+34 -32
View File
@@ -1,8 +1,11 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import { spawn } from 'child_process'; import fs from 'fs';
import path from 'path';
import SockService from './sock'; import SockService from './sock';
import CronService from './cron'; import CronService from './cron';
import ScheduleService, { TaskCallbacks } from './schedule';
import config from '../config';
@Service() @Service()
export default class ScriptService { export default class ScriptService {
@@ -10,44 +13,43 @@ export default class ScriptService {
@Inject('logger') private logger: winston.Logger, @Inject('logger') private logger: winston.Logger,
private sockService: SockService, private sockService: SockService,
private cronService: CronService, private cronService: CronService,
private scheduleService: ScheduleService,
) {} ) {}
public async runScript(path: string) { private taskCallbacks(filePath: string): TaskCallbacks {
const cp = spawn(`task -l ${path} now`, { shell: '/bin/bash' }); 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) => { public async runScript(filePath: string) {
this.sockService.sendMessage({ const relativePath = path.relative(config.scriptPath, filePath);
type: 'manuallyRunScript', const command = `task -l ${relativePath} now`;
message: data.toString(), this.scheduleService.runTask(command, this.taskCallbacks(filePath));
});
});
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: `执行结束`,
});
});
return { code: 200 }; return { code: 200 };
} }
public async stopScript(path: string) { public async stopScript(filePath: string) {
const err = await this.cronService.killTask(`task -l ${path} now`); const relativePath = path.relative(config.scriptPath, filePath);
const err = await this.cronService.killTask(`task -l ${relativePath} now`);
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';
this.sockService.sendMessage({ this.sockService.sendMessage({
type: 'manuallyRunScript', type: 'manuallyRunScript',
+3 -3
View File
@@ -28,6 +28,7 @@ import ScheduleService, { TaskCallbacks } from './schedule';
import { SimpleIntervalSchedule } from 'toad-scheduler'; import { SimpleIntervalSchedule } from 'toad-scheduler';
import SockService from './sock'; import SockService from './sock';
import SshKeyService from './sshKey'; import SshKeyService from './sshKey';
import dayjs from 'dayjs';
@Service() @Service()
export default class SubscriptionService { export default class SubscriptionService {
@@ -371,11 +372,10 @@ export default class SubscriptionService {
const err = await this.killTask(command); const err = await this.killTask(command);
const absolutePath = await this.handleLogPath(doc.log_path as string); const absolutePath = await this.handleLogPath(doc.log_path as string);
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';
fs.appendFileSync( fs.appendFileSync(
`${absolutePath}`, `${absolutePath}`,
`${str}\n## 执行结束... ${new Date() `${str}\n## 执行结束... ${dayjs().format('YYYY-MM-DD HH:mm:ss')} `,
.toLocaleString('zh', { hour12: false })
.replace(' 24:', ' 00:')} `,
); );
} }
+3 -3
View File
@@ -13,7 +13,7 @@ import { Request } from 'express';
import ScheduleService from './schedule'; import ScheduleService from './schedule';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import SockService from './sock'; import SockService from './sock';
import got from 'got'; import dayjs from 'dayjs';
@Service() @Service()
export default class UserService { export default class UserService {
@@ -111,7 +111,7 @@ export default class UserService {
}); });
await this.notificationService.notify( await this.notificationService.notify(
'登录通知', '登录通知',
`你于${new Date(timestamp).toLocaleString()}${address} ${ `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}${address} ${
req.platform req.platform
}端 登录成功,ip地址 ${ip}`, }端 登录成功,ip地址 ${ip}`,
); );
@@ -140,7 +140,7 @@ export default class UserService {
}); });
await this.notificationService.notify( await this.notificationService.notify(
'登录通知', '登录通知',
`你于${new Date(timestamp).toLocaleString()}${address} ${ `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}${address} ${
req.platform req.platform
}端 登录失败,ip地址 ${ip}`, }端 登录失败,ip地址 ${ip}`,
); );
+3 -7
View File
@@ -31,11 +31,7 @@
"allowedVersions": { "allowedVersions": {
"react": "17", "react": "17",
"react-dom": "17" "react-dom": "17"
}, }
"ignoreMissing": [
"monaco-editor",
"react-router"
]
} }
}, },
"dependencies": { "dependencies": {
@@ -63,10 +59,10 @@
"nodemailer": "^6.7.2", "nodemailer": "^6.7.2",
"p-queue": "7.2.0", "p-queue": "7.2.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"sequelize": "^6.17.0", "sequelize": "^6.20.1",
"serve-handler": "^6.1.3", "serve-handler": "^6.1.3",
"sockjs": "^0.3.24", "sockjs": "^0.3.24",
"sqlite3": "npm:@louislam/sqlite3@^15.0.3", "sqlite3": "npm:@louislam/sqlite3@^15.0.6",
"toad-scheduler": "^1.6.0", "toad-scheduler": "^1.6.0",
"typedi": "^0.10.0", "typedi": "^0.10.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
+1 -1
View File
@@ -39,7 +39,7 @@ AutoStartBot=""
BotRepoUrl="" BotRepoUrl=""
## 安装bot依赖时指定pip源,默认使用清华源,如不需要源,设置此参数为空 ## 安装bot依赖时指定pip源,默认使用清华源,如不需要源,设置此参数为空
PipMirror="https://pypi.tuna.tsinghua.edu.cn/simple" PipMirror="https://pypi.doubanio.com/simple/"
## 通知环境变量 ## 通知环境变量
## 1. Server酱 ## 1. Server酱
+8 -26
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
get_token() { get_token() {
token=$(cat $file_auth_user | jq -r .token) token=$(ts-node-transpile-only "$dir_shell/token.ts")
} }
add_cron_api() { add_cron_api() {
@@ -17,7 +17,7 @@ add_cron_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \
@@ -52,7 +52,7 @@ update_cron_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -84,7 +84,7 @@ update_cron_command_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -109,7 +109,7 @@ del_cron_api() {
local ids=$1 local ids=$1
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'DELETE' \ -X 'DELETE' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -130,24 +130,6 @@ del_cron_api() {
fi fi
} }
get_user_info() {
local currentTimeStamp=$(date +%s)
local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/user?t=$currentTimeStamp" \
-H 'Accept: */*' \
-H "Authorization: Bearer $token" \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36' \
-H 'Referer: http://0.0.0.0:5700/crontab' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7' \
--compressed
)
code=$(echo $api | jq -r .code)
if [[ $code != 200 ]]; then
echo -e "请先登录!"
exit 0
fi
}
update_cron() { update_cron() {
local ids="$1" local ids="$1"
local status="$2" local status="$2"
@@ -157,7 +139,7 @@ update_cron() {
local runningTime="${6:-0}" local runningTime="${6:-0}"
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons/status?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons/status?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -172,7 +154,7 @@ update_cron() {
code=$(echo $api | jq -r .code) code=$(echo $api | jq -r .code)
message=$(echo $api | jq -r .message) message=$(echo $api | jq -r .message)
if [[ $code != 200 ]]; then if [[ $code != 200 ]]; then
echo -e "\n## 更新任务状态失败(${message})\n" >> $log_path echo -e "\n## 更新任务状态失败(${message})\n" >> $dir_log/$log_path
fi fi
} }
@@ -181,7 +163,7 @@ notify_api() {
local content=$2 local content=$2
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/system/notify?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/system/notify?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
+1
View File
@@ -88,6 +88,7 @@ check_pm2() {
main() { main() {
echo -e "=====> 开始检测" echo -e "=====> 开始检测"
npm i -g pnpm
pnpm add -g pm2 pnpm add -g pm2
copy_dep copy_dep
check_ql check_ql
+50 -7
View File
@@ -14,7 +14,6 @@ dir_log=$dir_data/log
dir_db=$dir_data/db dir_db=$dir_data/db
dir_dep=$dir_data/deps dir_dep=$dir_data/deps
dir_list_tmp=$dir_log/.tmp dir_list_tmp=$dir_log/.tmp
dir_code=$dir_log/code
dir_update_log=$dir_log/update dir_update_log=$dir_log/update
ql_static_repo=$dir_repo/static ql_static_repo=$dir_repo/static
@@ -52,12 +51,10 @@ list_own_drop=$dir_list_tmp/own_drop.list
link_name=( link_name=(
task task
ql ql
notify
) )
original_name=( original_name=(
task.sh task.sh
update.sh update.sh
notify.sh
) )
init_env() { init_env() {
@@ -101,7 +98,7 @@ make_dir() {
} }
detect_termux() { detect_termux() {
if [[ ${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} ]] || [[ $PATH == *com.termux* ]]; then if [[ $PATH == *com.termux* ]]; then
is_termux=1 is_termux=1
else else
is_termux=0 is_termux=0
@@ -248,11 +245,11 @@ fix_config() {
npm_install_sub() { npm_install_sub() {
set_proxy set_proxy
if [ $is_termux -eq 1 ]; then if [ $is_termux -eq 1 ]; then
npm install --production --no-bin-links --registry=https://registry.npm.taobao.org || npm install --production --no-bin-links npm install --production --no-bin-links --registry=https://registry.npmmirror.com || npm install --production --no-bin-links
elif ! type pnpm &>/dev/null; then elif ! type pnpm &>/dev/null; then
npm install --production --registry=https://registry.npm.taobao.org || npm install --production npm install --production --registry=https://registry.npmmirror.com || npm install --production
else else
pnpm install --loglevel error --production --registry=https://registry.npm.taobao.org || pnpm install --production --loglevel error pnpm install --loglevel error --production --registry=https://registry.npmmirror.com || pnpm install --production --loglevel error
fi fi
unset_proxy unset_proxy
} }
@@ -380,6 +377,52 @@ reload_pm2() {
pm2 start $dir_static/build/public.js -n public --source-map-support --time &>/dev/null pm2 start $dir_static/build/public.js -n public --source-map-support --time &>/dev/null
} }
diff_time() {
local format="$1"
local begin_time="$2"
local end_time="$3"
if [[ $is_macos -eq 1 ]]; then
diff_time=$(($(date -j -f "$format" "$end_time" +%s) - $(date -j -f "$format" "$begin_time" +%s)))
else
diff_time=$(($(date +%s -d "$end_time") - $(date +%s -d "$begin_time")))
fi
echo "$diff_time"
}
format_time() {
local format="$1"
local time="$2"
if [[ $is_macos -eq 1 ]]; then
echo $(date -j -f "$format" "$time" "+%Y-%m-%d %H:%M:%S")
else
echo $(date -d "$time" "+%Y-%m-%d %H:%M:%S")
fi
}
format_log_time() {
local format="$1"
local time="$2"
if [[ $is_macos -eq 1 ]]; then
echo $(date -j -f "$format" "$time" "+%Y-%m-%d-%H-%M-%S")
else
echo $(date -d "$time" "+%Y-%m-%d-%H-%M-%S")
fi
}
format_timestamp() {
local format="$1"
local time="$2"
if [[ $is_macos -eq 1 ]]; then
echo $(date -j -f "$format" "$time" "+%s")
else
echo $(date -d "$time" "+%s")
fi
}
init_env init_env
detect_termux detect_termux
detect_macos detect_macos
+33 -25
View File
@@ -84,8 +84,8 @@ run_normal() {
fi fi
fi fi
local time=$(date) local time=$(date "+$time_format")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time=$(format_log_time "$time_format" "$time")
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -99,12 +99,13 @@ run_normal() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "$time_format" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "$time_format" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -118,12 +119,13 @@ run_normal() {
cd ${relative_path} cd ${relative_path}
file_param=${file_param/$relative_path\//} file_param=${file_param/$relative_path\//}
fi fi
eval timeout -k 10s $command_timeout_time $which_program $file_param $cmd
eval $timeoutCmd $which_program $file_param $cmd
eval . $file_task_after "$@" $cmd eval . $file_task_after "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
local end_timestamp=$(date "+%s") local end_timestamp=$(date "+%s")
local diff_time=$(( $end_timestamp - $begin_timestamp )) local diff_time=$(expr $end_timestamp - $begin_timestamp)
[[ $id ]] && update_cron "\"$id\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time" [[ $id ]] && update_cron "\"$id\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time"
eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time" $cmd eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time" $cmd
} }
@@ -154,8 +156,8 @@ run_concurrent() {
[[ ! -z $cookieStr ]] && export ${env_param}=${cookieStr} [[ ! -z $cookieStr ]] && export ${env_param}=${cookieStr}
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+$time_format")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time=$(format_log_time "$time_format" "$time")
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -169,12 +171,12 @@ run_concurrent() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "$time_format" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "$time_format" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -196,7 +198,7 @@ run_concurrent() {
for i in "${!array[@]}"; do for i in "${!array[@]}"; do
export ${env_param}=${array[i]} export ${env_param}=${array[i]}
single_log_path="$dir_log/$log_dir/${single_log_time}_$((i + 1)).log" single_log_path="$dir_log/$log_dir/${single_log_time}_$((i + 1)).log"
timeout -k 10s $command_timeout_time $which_program $file_param &>$single_log_path & eval $timeoutCmd $which_program $file_param &>$single_log_path &
done done
wait wait
@@ -224,8 +226,8 @@ run_designated() {
fi fi
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+$time_format")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time=$(format_log_time "$time_format" "$time")
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -239,12 +241,12 @@ run_designated() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "$time_format" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "$time_format" "$time")
local envs=$(eval echo "\$${env_param}") local envs=$(eval echo "\$${env_param}")
local array=($(echo $envs | sed 's/&/ /g')) local array=($(echo $envs | sed 's/&/ /g'))
@@ -274,7 +276,7 @@ run_designated() {
cd ${relative_path} cd ${relative_path}
file_param=${file_param/$relative_path\//} file_param=${file_param/$relative_path\//}
fi fi
eval timeout -k 10s $command_timeout_time $which_program $file_param $cmd eval $timeoutCmd $which_program $file_param $cmd
eval . $file_task_after "$@" $cmd eval . $file_task_after "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
@@ -288,8 +290,8 @@ run_designated() {
run_else() { run_else() {
local file_param="$1" local file_param="$1"
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+$time_format")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time=$(format_log_time "$time_format" "$time")
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -303,12 +305,12 @@ run_else() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "$time_format" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "$time_format" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -325,7 +327,7 @@ run_else() {
fi fi
shift shift
eval timeout -k 10s $command_timeout_time $which_program "$file_param" "$@" $cmd eval $timeoutCmd $which_program "$file_param" "$@" $cmd
eval . $file_task_after "$file_param" "$@" $cmd eval . $file_task_after "$file_param" "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
@@ -348,6 +350,12 @@ main() {
done done
[[ "$show_log" == "true" ]] && shift $(($OPTIND - 1)) [[ "$show_log" == "true" ]] && shift $(($OPTIND - 1))
timeoutCmd=""
if type timeout &>/dev/null; then
timeoutCmd="timeout -k 10s $command_timeout_time "
fi
time_format="%Y-%m-%d %H:%M:%S"
if [[ $1 == *.js ]] || [[ $1 == *.py ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then if [[ $1 == *.js ]] || [[ $1 == *.py ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then
case $# in case $# in
1) 1)
Executable
+56
View File
@@ -0,0 +1,56 @@
import 'reflect-metadata';
import OpenService from '../back/services/open';
import { Container } from 'typedi';
import LoggerInstance from '../back/loaders/logger';
import fs from 'fs';
import config from '../back/config';
import path from 'path';
const tokenFile = path.join(config.configPath, 'token.json');
async function getToken() {
try {
const data = await readFile();
const nowTime = Math.round(Date.now() / 1000);
if (data.value && data.expiration > nowTime) {
console.log(data.value);
} else {
Container.set('logger', LoggerInstance);
const openService = Container.get(OpenService);
const appToken = await openService.findSystemToken();
console.log(appToken.value);
await writeFile({
value: appToken.value,
expiration: appToken.expiration,
});
}
} catch (error) {
console.log(error);
}
}
async function readFile() {
return new Promise<any>((resolve, reject) => {
fs.readFile(
path.join(config.configPath, 'token.json'),
{ encoding: 'utf8' },
(err, data) => {
if (err) {
resolve({});
} else {
resolve(JSON.parse(data));
}
},
);
});
}
async function writeFile(data: any) {
return new Promise<void>((resolve, reject) => {
fs.writeFile(tokenFile, JSON.stringify(data), { encoding: 'utf8' }, () => {
resolve();
});
});
}
getToken();
+8 -9
View File
@@ -12,14 +12,16 @@ diff_cron() {
local list_task="$2" local list_task="$2"
local list_add="$3" local list_add="$3"
local list_drop="$4" local list_drop="$4"
if [[ -s $list_task ]]; then if [[ -s $list_task ]] && [[ -s $list_scripts ]]; then
grep -vwf $list_task $list_scripts >$list_add grep -vwf $list_task $list_scripts >$list_add
elif [[ ! -s $list_task ]] && [[ -s $list_scripts ]]; then grep -vwf $list_scripts $list_task >$list_drop
fi
if [[ ! -s $list_task ]] && [[ -s $list_scripts ]]; then
cp -f $list_scripts $list_add cp -f $list_scripts $list_add
fi fi
if [[ -s $list_scripts ]]; then
grep -vwf $list_scripts $list_task >$list_drop if [[ ! -s $list_scripts ]] && [[ -s $list_task ]]; then
else
cp -f $list_task $list_drop cp -f $list_task $list_drop
fi fi
} }
@@ -305,7 +307,6 @@ patch_version() {
fi fi
# 兼容pnpm@7 # 兼容pnpm@7
npm i -g pnpm
pnpm setup pnpm setup
source ~/.bashrc source ~/.bashrc
@@ -487,7 +488,6 @@ main() {
run_extra_shell >>$log_path run_extra_shell >>$log_path
;; ;;
repo) repo)
get_user_info
get_uniq_path "$p2" "$p6" get_uniq_path "$p2" "$p6"
if [[ -n $p2 ]]; then if [[ -n $p2 ]]; then
update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7" update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7"
@@ -497,7 +497,6 @@ main() {
fi fi
;; ;;
raw) raw)
get_user_info
get_uniq_path "$p2" get_uniq_path "$p2"
if [[ -n $p2 ]]; then if [[ -n $p2 ]]; then
update_raw "$p2" update_raw "$p2"
@@ -539,7 +538,7 @@ main() {
;; ;;
esac esac
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
local diff_time=$(($(date +%s -d "$end_time") - $(date +%s -d "$begin_time"))) local diff_time=$(diff_time "%Y-%m-%d %H:%M:%S" "$begin_time" "$end_time")
if [[ $p1 != "repo" ]] && [[ $p1 != "raw" ]]; then if [[ $p1 != "repo" ]] && [[ $p1 != "raw" ]]; then
echo -e "\n## 执行结束... $end_time 耗时 $diff_time" >>$log_path echo -e "\n## 执行结束... $end_time 耗时 $diff_time" >>$log_path
cat $log_path cat $log_path
+22 -8
View File
@@ -20,6 +20,7 @@ import {
FileOutlined, FileOutlined,
PlayCircleOutlined, PlayCircleOutlined,
PauseCircleOutlined, PauseCircleOutlined,
FullscreenOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { CrontabStatus } from './index'; import { CrontabStatus } from './index';
import { diffTime } from '@/utils/date'; import { diffTime } from '@/utils/date';
@@ -106,7 +107,8 @@ const CronDetailModal = ({
glyphMargin: false, glyphMargin: false,
wordWrap: 'on', wordWrap: 'on',
}} }}
onMount={(editor) => { onMount={(editor, monaco) => {
console.log(monaco);
editorRef.current = editor; editorRef.current = editor;
}} }}
/> />
@@ -352,6 +354,11 @@ const CronDetailModal = ({
}); });
}; };
const fullscreen = () => {
const editorElement = editorRef.current._domElement as HTMLElement;
editorElement.parentElement?.requestFullscreen();
};
useEffect(() => { useEffect(() => {
if (cron && cron.id) { if (cron && cron.id) {
setCurrentCron(cron); setCurrentCron(cron);
@@ -529,13 +536,20 @@ const CronDetailModal = ({
}} }}
tabBarExtraContent={ tabBarExtraContent={
activeTabKey === 'script' && ( activeTabKey === 'script' && (
<Button <>
type="primary" <Button
style={{ marginRight: 8 }} type="primary"
onClick={saveFile} style={{ marginRight: 8 }}
> onClick={saveFile}
>
</Button>
</Button>
<Button
type="primary"
icon={<FullscreenOutlined />}
onClick={fullscreen}
/>
</>
) )
} }
> >
+15 -1
View File
@@ -25,7 +25,7 @@ import EditNameModal from './editNameModal';
import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import './index.less'; import './index.less';
import { getTableScroll } from '@/utils/index'; import { exportJson, getTableScroll } from '@/utils/index';
const { Text, Paragraph } = Typography; const { Text, Paragraph } = Typography;
const { Search } = Input; const { Search } = Input;
@@ -462,6 +462,13 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
}); });
}; };
const exportEnvs = () => {
const envs = value
.filter((x) => selectedRowIds.includes(x.id))
.map((x) => ({ value: x.value, name: x.name, remarks: x.remarks }));
exportJson('env.json', JSON.stringify(envs));
};
const modifyName = () => { const modifyName = () => {
setIsEditNameModalVisible(true); setIsEditNameModalVisible(true);
}; };
@@ -516,6 +523,13 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
> >
</Button> </Button>
<Button
type="primary"
onClick={() => exportEnvs()}
style={{ marginLeft: 8, marginRight: 8 }}
>
</Button>
<Button <Button
type="primary" type="primary"
onClick={() => operateEnvs(0)} onClick={() => operateEnvs(0)}
+2 -1
View File
@@ -60,7 +60,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
const getLog = (node: any) => { const getLog = (node: any) => {
request request
.get(`${config.apiPrefix}logs/${node.value}?path=${node.parent || ''}`) .get(`${config.apiPrefix}logs/${node.title}?path=${node.parent || ''}`)
.then((data) => { .then((data) => {
setValue(data.data); setValue(data.data);
}); });
@@ -113,6 +113,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data} treeData={data}
placeholder="请选择日志" placeholder="请选择日志"
fieldNames={{ value: 'key', label: 'title' }}
showSearch showSearch
onSelect={onSelect} onSelect={onSelect}
/>, />,
+11 -7
View File
@@ -66,7 +66,7 @@ const EditModal = ({
const getDetail = (node: any) => { const getDetail = (node: any) => {
request request
.get(`${config.apiPrefix}scripts/${node.value}?path=${node.parent || ''}`) .get(`${config.apiPrefix}scripts/${node.title}?path=${node.parent || ''}`)
.then((data) => { .then((data) => {
setValue(data.data); setValue(data.data);
}); });
@@ -74,11 +74,13 @@ const EditModal = ({
const run = () => { const run = () => {
setLog(''); setLog('');
const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
request request
.put(`${config.apiPrefix}scripts/run`, { .put(`${config.apiPrefix}scripts/run`, {
data: { data: {
filename: cNode.value, filename: cNode.title,
path: cNode.parent || '', path: cNode.parent || '',
content,
}, },
}) })
.then((data) => { .then((data) => {
@@ -87,14 +89,16 @@ const EditModal = ({
}; };
const stop = () => { const stop = () => {
if (!cNode || !cNode.value) { if (!cNode || !cNode.title) {
return; return;
} }
const content = editorRef.current.getValue().replace(/\r\n/g, '\n');
request request
.put(`${config.apiPrefix}scripts/stop`, { .put(`${config.apiPrefix}scripts/stop`, {
data: { data: {
filename: cNode.value, filename: cNode.title,
path: cNode.parent || '', path: cNode.parent || '',
content,
}, },
}) })
.then((data) => { .then((data) => {
@@ -113,8 +117,7 @@ const EditModal = ({
return; return;
} }
if (_message === '执行结束') { if (_message.includes('执行结束')) {
_message = '';
setTimeout(() => { setTimeout(() => {
setIsRunning(false); setIsRunning(false);
}, 300); }, 300);
@@ -146,6 +149,7 @@ const EditModal = ({
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData} treeData={treeData}
placeholder="请选择脚本文件" placeholder="请选择脚本文件"
fieldNames={{ value: 'key', label: 'title' }}
showSearch showSearch
onSelect={onSelect} onSelect={onSelect}
/> />
@@ -243,7 +247,7 @@ const EditModal = ({
content: content:
editorRef.current && editorRef.current &&
editorRef.current.getValue().replace(/\r\n/g, '\n'), editorRef.current.getValue().replace(/\r\n/g, '\n'),
filename: cNode?.value, filename: cNode?.title,
}} }}
/> />
<SettingModal <SettingModal
+89 -22
View File
@@ -1,7 +1,17 @@
import React, { useEffect, useState } from 'react'; 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 { request } from '@/utils/http';
import config from '@/utils/config'; import config from '@/utils/config';
import { UploadOutlined } from '@ant-design/icons';
const { Option } = Select; const { Option } = Select;
@@ -21,21 +31,30 @@ const EditScriptNameModal = ({
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [dirs, setDirs] = useState<any[]>([]); const [dirs, setDirs] = useState<any[]>([]);
const [file, setFile] = useState<File>();
const [type, setType] = useState<'blank' | 'upload'>('blank');
const handleOk = async (values: any) => { const handleOk = async (values: any) => {
setLoading(true); setLoading(true);
values.path = values.path || ''; 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 request
.post(`${config.apiPrefix}scripts`, { .post(`${config.apiPrefix}scripts`, {
data: { filename: values.filename, path: values.path, content: '' }, data: formData,
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success('新建文件成功');
const key = values.path ? `${values.path}/` : '';
const filename = file ? file.name : values.filename;
handleCancel({ handleCancel({
filename: values.filename, filename,
path: values.path, path: values.path,
key: `${values.path}-${values.filename}`, key: `${key}${filename}`,
}); });
} else { } else {
message.error(data); message.error(data);
@@ -45,15 +64,42 @@ const EditScriptNameModal = ({
.finally(() => setLoading(false)); .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(() => { useEffect(() => {
form.resetFields(); form.resetFields();
const originDirs = treeData.filter((x) => x.disabled);
setDirs([{ key: '' }, ...originDirs]);
}, [visible]); }, [visible]);
return ( return (
<Modal <Modal
title="新建文件" title="新建脚本"
visible={visible} visible={visible}
forceRender forceRender
centered centered
@@ -73,23 +119,44 @@ const EditScriptNameModal = ({
> >
<Form form={form} layout="vertical" name="edit_name_modal"> <Form form={form} layout="vertical" name="edit_name_modal">
<Form.Item <Form.Item
name="filename" name="type"
label="文件名" label="类型"
rules={[{ required: true, message: '请输入文件名' }]} 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>
<Form.Item {type === 'blank' && (
label="父目录" <Form.Item
name="path" name="filename"
initialValue={dirs && dirs.length > 0 ? dirs[0].key : ''} label="文件名"
> rules={[{ required: true, message: '请输入文件名' }]}
<Select placeholder="请选择父目录"> >
{dirs.map((x) => ( <Input placeholder="请输入文件名" />
<Option value={x.key}>{x.key || '根'}</Option> </Form.Item>
))} )}
</Select> <Form.Item label="父目录" name="path">
<TreeSelect
allowClear
treeData={dirs}
fieldNames={{ value: 'key', label: 'title' }}
placeholder="请选择父目录"
treeDefaultExpandAll
/>
</Form.Item> </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> </Form>
</Modal> </Modal>
); );
+10 -9
View File
@@ -104,7 +104,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const getDetail = (node: any) => { const getDetail = (node: any) => {
request request
.get(`${config.apiPrefix}scripts/${node.value}?path=${node.parent || ''}`) .get(`${config.apiPrefix}scripts/${node.title}?path=${node.parent || ''}`)
.then((data) => { .then((data) => {
setValue(data.data); setValue(data.data);
}); });
@@ -117,7 +117,6 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const obj = { const obj = {
node: { node: {
title: s, title: s,
value: s,
key: p ? vkey : s, key: p ? vkey : s,
parent: p, parent: p,
}, },
@@ -209,7 +208,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
<> <>
<Text style={{ wordBreak: 'break-all' }} type="warning"> <Text style={{ wordBreak: 'break-all' }} type="warning">
{currentNode.value} {currentNode.title}
</Text>{' '} </Text>{' '}
</> </>
@@ -222,7 +221,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request request
.put(`${config.apiPrefix}scripts`, { .put(`${config.apiPrefix}scripts`, {
data: { data: {
filename: currentNode.value, filename: currentNode.title,
path: currentNode.parent || '', path: currentNode.parent || '',
content, content,
}, },
@@ -262,7 +261,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request request
.delete(`${config.apiPrefix}scripts`, { .delete(`${config.apiPrefix}scripts`, {
data: { data: {
filename: currentNode.value, filename: currentNode.title,
path: currentNode.parent || '', path: currentNode.parent || '',
}, },
}) })
@@ -315,8 +314,9 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
) => { ) => {
if (filename) { if (filename) {
const newData = [...data]; const newData = [...data];
const _file = { title: filename, key, value: filename, parent: path }; const _file = { title: filename, key, parent: path };
if (path) { if (path) {
// TODO: 更新左侧树数据
const parentNodeIndex = newData.findIndex((x) => x.key === path); const parentNodeIndex = newData.findIndex((x) => x.key === path);
if (parentNodeIndex !== -1) { if (parentNodeIndex !== -1) {
const parentNode = newData[parentNodeIndex]; const parentNode = newData[parentNodeIndex];
@@ -331,7 +331,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
newData.unshift(_file); newData.unshift(_file);
} }
setData(newData); setData(newData);
onSelect(_file.value, _file); onSelect(_file.title, _file);
setIsEditing(true); setIsEditing(true);
} }
setIsAddFileModalVisible(false); setIsAddFileModalVisible(false);
@@ -341,7 +341,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
request request
.post(`${config.apiPrefix}scripts/download`, { .post(`${config.apiPrefix}scripts/download`, {
data: { data: {
filename: currentNode.value, filename: currentNode.title,
}, },
}) })
.then((_data: any) => { .then((_data: any) => {
@@ -349,7 +349,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = currentNode.value; a.download = currentNode.title;
document.documentElement.appendChild(a); document.documentElement.appendChild(a);
a.click(); a.click();
document.documentElement.removeChild(a); document.documentElement.removeChild(a);
@@ -451,6 +451,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data} treeData={data}
placeholder="请选择脚本" placeholder="请选择脚本"
fieldNames={{ value: 'key', label: 'title' }}
showSearch showSearch
onSelect={onSelect} onSelect={onSelect}
/>, />,
+9 -6
View File
@@ -204,13 +204,15 @@ const SubscriptionModal = ({
] = text ] = text
.split(' ') .split(' ')
.map((x) => x.trim().replace(/\"/g, '').replace(/\'/, '')); .map((x) => x.trim().replace(/\"/g, '').replace(/\'/, ''));
const _type =
type === 'raw'
? 'file'
: url.startsWith('http')
? 'public-repo'
: 'private-repo';
form.setFieldsValue({ form.setFieldsValue({
type: type: _type,
type === 'raw'
? 'file'
: url.startsWith('http')
? 'public-repo'
: 'private-repo',
url, url,
whitelist, whitelist,
blacklist, blacklist,
@@ -219,6 +221,7 @@ const SubscriptionModal = ({
extensions, extensions,
alias: formatAlias(url, branch), alias: formatAlias(url, branch),
}); });
setType(_type);
} }
}, []); }, []);
+36
View File
@@ -205,3 +205,39 @@ export function getTableScroll({
let height = document.body.clientHeight - mainTop - extraHeight; let height = document.body.clientHeight - mainTop - extraHeight;
return height; return height;
} }
// 自动触发点击事件
function automaticClick(elment: HTMLElement) {
const ev = document.createEvent('MouseEvents');
ev.initMouseEvent(
'click',
true,
false,
window,
0,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null,
);
elment.dispatchEvent(ev);
}
// 导出文件
export function exportJson(name: string, data: string) {
const urlObject = window.URL || window.webkitURL || window;
const export_blob = new Blob([data]);
const createA = document.createElementNS(
'http://www.w3.org/1999/xhtml',
'a',
) as any;
createA.href = urlObject.createObjectURL(export_blob);
createA.download = name;
automaticClick(createA);
}
+4 -7
View File
@@ -1,8 +1,5 @@
export const version = '2.13.2'; export const version = '2.13.5';
export const changeLogLink = 'https://t.me/jiao_long/303'; export const changeLogLink = 'https://t.me/jiao_long/320';
export const changeLog = `2.13.2 版本说明 export const changeLog = `2.13.5 版本说明
1. 修复nodejs依赖安装失败pnpm setup 1. 修复task命令获取时间异常
2. 修复订阅禁用失效
3. 修复新建订阅定时验证
4. 修复ssh配置文件更新逻辑
`; `;