Compare commits

..

12 Commits

Author SHA1 Message Date
whyour 99d7eaeff0 更新版本 v2.13.1 2022-05-29 01:50:46 +08:00
whyour c8f5eb7c2e 修复pnpm版本兼容 2022-05-29 01:50:05 +08:00
whyour 109e16e8fb 修复前端列表查询 2022-05-28 21:49:34 +08:00
whyour 555f6e04e8 修复依赖日志查看 2022-05-28 21:10:24 +08:00
whyour bcde0a54bf 修复依赖安装日志 2022-05-28 20:51:25 +08:00
whyour eee7f25bc3 修复订阅日志查看 2022-05-28 20:11:38 +08:00
whyour b10157b09a 修复系统启动执行订阅任务 2022-05-28 19:56:12 +08:00
whyour a09a21fa5d 依赖改为串行安装 2022-05-28 19:41:37 +08:00
whyour ec2918b427 修改订阅日志排版 2022-05-28 19:04:46 +08:00
whyour 57bfc19ecf 修复execSync卡死主进程 2022-05-28 18:55:52 +08:00
whyour aa110d4187 修复任务详情日志访问,修复新建订阅粘贴repo命令 2022-05-28 17:36:41 +08:00
whyour 9e8febfc11 修复七牛sdk参数 2022-05-28 00:11:43 +08:00
16 changed files with 244 additions and 156 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ export default (app: Router) => {
try {
const filePath = join(
config.logPath,
req.query.path as string,
(req.query.path || '') as string,
req.params.file,
);
const content = getFileContentByName(filePath);
+1 -5
View File
@@ -27,11 +27,7 @@ export default async () => {
if (Object.prototype.hasOwnProperty.call(groups, key)) {
const group = groups[key];
const depIds = group.map((x) => x.id);
for (const dep of depIds) {
if (dep) {
await dependenceService.reInstall([dep]);
}
}
await dependenceService.reInstall(depIds as number[]);
}
}
});
+1 -1
View File
@@ -26,6 +26,6 @@ export default async () => {
// 运行所有订阅
const subs = await subscriptionService.list();
for (const sub of subs) {
await subscriptionService.handleTask(sub);
await subscriptionService.handleTask(sub, true, true, true);
}
};
+19 -4
View File
@@ -13,6 +13,7 @@ import _ from 'lodash';
import { spawn } from 'child_process';
import SockService from './sock';
import { Op } from 'sequelize';
import { concurrentRun } from '../config/util';
@Service()
export default class DependenceService {
@@ -27,7 +28,7 @@ export default class DependenceService {
return tab;
});
const docs = await this.insert(tabs);
this.installOrUninstallDependencies(docs);
this.installDependenceOneByOne(docs);
return docs;
}
@@ -47,7 +48,7 @@ export default class DependenceService {
status: DependenceStatus.installing,
});
const newDoc = await this.updateDb(tab);
this.installOrUninstallDependencies([newDoc]);
this.installDependenceOneByOne([newDoc]);
return newDoc;
}
@@ -62,7 +63,7 @@ export default class DependenceService {
{ where: { id: ids } },
);
const docs = await DependenceModel.findAll({ where: { id: ids } });
this.installOrUninstallDependencies(docs, false, force);
this.installDependenceOneByOne(docs, false, force);
return docs;
}
@@ -98,6 +99,20 @@ export default class DependenceService {
}
}
private installDependenceOneByOne(
docs: Dependence[],
isInstall: boolean = true,
force: boolean = false,
) {
concurrentRun(
docs.map(
(dep) => async () =>
await this.installOrUninstallDependencies([dep], isInstall, force),
),
1,
);
}
public async reInstall(ids: number[]): Promise<Dependence[]> {
await DependenceModel.update(
{ status: DependenceStatus.installing, log: [] },
@@ -105,7 +120,7 @@ export default class DependenceService {
);
const docs = await DependenceModel.findAll({ where: { id: ids } });
this.installOrUninstallDependencies(docs);
this.installDependenceOneByOne(docs);
return docs;
}
+14 -1
View File
@@ -106,6 +106,7 @@ export default class ScheduleService {
async createCronTask(
{ id = 0, command, name, schedule = '' }: ScheduleTaskType,
callbacks?: TaskCallbacks,
runImmediately = false,
) {
const _id = this.formatId(id);
this.logger.info(
@@ -122,6 +123,10 @@ export default class ScheduleService {
await this.runTask(command, callbacks);
}),
);
if (runImmediately) {
await this.runTask(command, callbacks);
}
}
async cancelCronTask({ id = 0, name }: ScheduleTaskType) {
@@ -160,9 +165,17 @@ export default class ScheduleService {
},
);
const job = new LongIntervalJob({ ...schedule, runImmediately }, task, _id);
const job = new LongIntervalJob(
{ ...schedule, runImmediately: false },
task,
_id,
);
this.intervalSchedule.addIntervalJob(job);
if (runImmediately) {
await this.runTask(command, callbacks);
}
}
async cancelIntervalTask({ id = 0, name }: ScheduleTaskType) {
+70 -50
View File
@@ -124,7 +124,12 @@ export default class SubscriptionService {
return { url, host };
}
public handleTask(doc: Subscription, needCreate = true, needAddKey = true) {
public async handleTask(
doc: Subscription,
needCreate = true,
needAddKey = true,
runImmediately = false,
) {
const { url, host } = this.formatUrl(doc);
if (doc.type === 'private-repo' && doc.pull_type === 'ssh-key') {
if (needAddKey) {
@@ -143,23 +148,36 @@ export default class SubscriptionService {
if (doc.schedule_type === 'crontab') {
this.scheduleService.cancelCronTask(doc as any);
needCreate &&
this.scheduleService.createCronTask(
(await this.scheduleService.createCronTask(
doc as any,
this.taskCallbacks(doc),
);
runImmediately,
));
} else {
this.scheduleService.cancelIntervalTask(doc as any);
const { type, value } = doc.interval_schedule as any;
needCreate &&
this.scheduleService.createIntervalTask(
(await this.scheduleService.createIntervalTask(
doc as any,
{ [type]: value } as SimpleIntervalSchedule,
true,
runImmediately,
this.taskCallbacks(doc),
);
));
}
}
private async 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));
},
);
});
}
private async handleLogPath(
logPath: string,
data: string = '',
@@ -175,27 +193,8 @@ export default class SubscriptionService {
private taskCallbacks(doc: Subscription): TaskCallbacks {
return {
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
// 执行sub_before
let beforeStr = '';
try {
if (doc.sub_before) {
beforeStr = execSync(doc.sub_before).toString();
}
} catch (error) {
beforeStr = JSON.stringify(error);
}
if (beforeStr) {
beforeStr += '\n';
}
const logTime = startTime.format('YYYY-MM-DD-HH-mm-ss');
const logPath = `${doc.alias}/${logTime}.log`;
await this.handleLogPath(
logPath as string,
`${beforeStr}## 开始执行... ${startTime.format(
'YYYY-MM-DD HH:mm:ss',
)}\n`,
);
await SubscriptionModel.update(
{
status: SubscriptionStatus.running,
@@ -204,14 +203,45 @@ export default class SubscriptionService {
},
{ where: { id: doc.id } },
);
const absolutePath = await this.handleLogPath(
logPath as string,
`## 开始执行... ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n`,
);
// 执行sub_before
let beforeStr = '';
try {
if (doc.sub_before) {
fs.appendFileSync(absolutePath, `\n## 执行before命令...\n\n`);
beforeStr = await this.promiseExec(doc.sub_before);
}
} catch (error: any) {
beforeStr =
(error.stderr && error.stderr.toString()) || JSON.stringify(error);
}
if (beforeStr) {
fs.appendFileSync(absolutePath, `${beforeStr}\n\n`);
}
},
onEnd: async (cp, endTime, diff) => {
const sub = await this.getDb({ id: doc.id });
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: undefined },
{ where: { id: sub.id } },
);
const absolutePath = await this.handleLogPath(sub.log_path as string);
// 执行 sub_after
let afterStr = '';
try {
if (sub.sub_after) {
fs.appendFileSync(absolutePath, `\n\n## 执行after命令...\n`);
afterStr = await this.promiseExec(sub.sub_after);
}
} catch (error: any) {
afterStr =
(error.stderr && error.stderr.toString()) || JSON.stringify(error);
}
if (afterStr) {
fs.appendFileSync(absolutePath, `${afterStr}\n`);
}
fs.appendFileSync(
absolutePath,
`\n## 执行结束... ${endTime.format(
@@ -219,20 +249,10 @@ export default class SubscriptionService {
)} 耗时 ${diff}`,
);
// 执行 sub_after
let afterStr = '';
try {
if (sub.sub_after) {
afterStr = execSync(sub.sub_after).toString();
}
} catch (error) {
afterStr = JSON.stringify(error);
}
if (afterStr) {
afterStr = `\n\n${afterStr}`;
const absolutePath = await this.handleLogPath(sub.log_path as string);
fs.appendFileSync(absolutePath, afterStr);
}
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: undefined },
{ where: { id: sub.id } },
);
this.sockService.sendMessage({
type: 'runSubscriptionEnd',
@@ -256,7 +276,7 @@ export default class SubscriptionService {
public async create(payload: Subscription): Promise<Subscription> {
const tab = new Subscription(payload);
const doc = await this.insert(tab);
this.handleTask(doc);
await this.handleTask(doc);
return doc;
}
@@ -266,7 +286,7 @@ export default class SubscriptionService {
public async update(payload: Subscription): Promise<Subscription> {
const newDoc = await this.updateDb(payload);
this.handleTask(newDoc);
await this.handleTask(newDoc);
return newDoc;
}
@@ -309,7 +329,7 @@ export default class SubscriptionService {
public async remove(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc, false, false);
await this.handleTask(doc, false, false);
}
await SubscriptionModel.destroy({ where: { id: ids } });
}
@@ -340,7 +360,7 @@ export default class SubscriptionService {
this.logger.silly(error);
}
}
this.handleTask(doc, false);
await this.handleTask(doc, false);
const command = this.formatCommand(doc);
const err = await this.killTask(command);
const absolutePath = await this.handleLogPath(doc.log_path as string);
@@ -411,7 +431,7 @@ export default class SubscriptionService {
public async disabled(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc, false);
await this.handleTask(doc, false);
}
await SubscriptionModel.update({ is_disabled: 1 }, { where: { id: ids } });
}
@@ -419,14 +439,14 @@ export default class SubscriptionService {
public async enabled(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc);
await this.handleTask(doc);
}
await SubscriptionModel.update({ is_disabled: 0 }, { where: { id: ids } });
}
public async log(id: number) {
const doc = await this.getDb({ id });
if (!doc) {
if (!doc || !doc.log_path) {
return '';
}
+1 -1
View File
@@ -17,7 +17,7 @@ const config = new qiniu.conf.Config({ zone: qiniu.zone.Zone_z1 });
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra(
'',
'',
{},
'text/plain; charset=utf-8',
);
// 文件上传
+1
View File
@@ -305,6 +305,7 @@ patch_version() {
fi
# 兼容pnpm@7
npm i -g pnpm
pnpm setup
source ~/.bashrc
+6 -2
View File
@@ -115,9 +115,13 @@ const CronDetailModal = ({
const onClickItem = (item: LogItem) => {
localStorage.setItem('logCron', currentCron.id);
setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`);
setLogUrl(
`${config.apiPrefix}logs/${item.filename}?path=${item.directory || ''}`,
);
request
.get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`)
.get(
`${config.apiPrefix}logs/${item.filename}?path=${item.directory || ''}`,
)
.then((data) => {
setLog(data.data);
setIsLogModalVisible(true);
+40 -28
View File
@@ -438,8 +438,10 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
message.success('删除成功');
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1);
setValue(result);
if (i !== -1) {
result.splice(i, 1);
setValue(result);
}
} else {
message.error(data);
}
@@ -470,11 +472,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
status: CrontabStatus.running,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
status: CrontabStatus.running,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -505,12 +509,14 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
pid: null,
status: CrontabStatus.idle,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
pid: null,
status: CrontabStatus.idle,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -550,11 +556,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const newStatus = record.isDisabled === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
isDisabled: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
isDisabled: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -594,11 +602,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const newStatus = record.isPinned === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
isPinned: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
isPinned: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -709,11 +719,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
.parseExpression(data.data.schedule)
.next()
.toDate();
result.splice(index, 1, {
...cron,
...data.data,
});
setValue(result);
if (index !== -1) {
result.splice(index, 1, {
...cron,
...data.data,
});
setValue(result);
}
})
.finally(() => setLoading(false));
};
+25 -15
View File
@@ -271,9 +271,11 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
result.push(...dependence);
} else {
const index = value.findIndex((x) => x.id === dependence.id);
result.splice(index, 1, {
...dependence,
});
if (index !== -1) {
result.splice(index, 1, {
...dependence,
});
}
}
setValue(result);
};
@@ -324,11 +326,13 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
.then((data: any) => {
const index = value.findIndex((x) => x.id === dependence.id);
const result = [...value];
result.splice(index, 1, {
...dependence,
...data.data,
});
setValue(result);
if (index !== -1) {
result.splice(index, 1, {
...dependence,
...data.data,
});
setValue(result);
}
})
.finally(() => setLoading(false));
};
@@ -371,10 +375,12 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
const result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
result.splice(index, 1, {
...result[index],
status,
});
if (index !== -1) {
result.splice(index, 1, {
...value[index],
status,
});
}
}
setValue(result);
@@ -383,7 +389,9 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
const _result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
_result.splice(index, 1);
if (index !== -1) {
_result.splice(index, 1);
}
}
setValue(_result);
}, 5000);
@@ -483,8 +491,10 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
if (needRemove) {
const index = value.findIndex((x) => x.id === logDependence.id);
const result = [...value];
result.splice(index, 1);
setValue(result);
if (index !== -1) {
result.splice(index, 1);
setValue(result);
}
} else if ([...value].map((x) => x.id).includes(logDependence.id)) {
getDependenceDetail(logDependence);
}
+8 -6
View File
@@ -90,17 +90,19 @@ const DependenceLogModal = ({
}, [dependence]);
useEffect(() => {
if (!socketMessage) return;
if (!socketMessage || !dependence) return;
const { type, message, references } = socketMessage;
if (
type === 'installDependence' &&
message.includes('结束时间') &&
references.length > 0
references.length > 0 &&
references.includes(dependence.id)
) {
setExecuting(false);
setIsRemoveFailed(message.includes('删除失败'));
if (message.includes('结束时间')) {
setExecuting(false);
setIsRemoveFailed(message.includes('删除失败'));
}
setValue(`${value}${message}`);
}
setValue(`${value}${message}`);
}, [socketMessage]);
useEffect(() => {
+15 -9
View File
@@ -278,13 +278,17 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const index = parentNode.children.findIndex(
(y) => y.key === currentNode.key,
);
parentNode.children.splice(index, 1);
newData.splice(parentNodeIndex, 1, { ...parentNode });
if (index !== -1 && parentNodeIndex !== -1) {
parentNode.children.splice(index, 1);
newData.splice(parentNodeIndex, 1, { ...parentNode });
}
} else {
const index = newData.findIndex(
(x) => x.key === currentNode.key,
);
newData.splice(index, 1);
if (index !== -1) {
newData.splice(index, 1);
}
}
setData(newData);
} else {
@@ -314,13 +318,15 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const _file = { title: filename, key, value: filename, parent: path };
if (path) {
const parentNodeIndex = newData.findIndex((x) => x.key === path);
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
if (parentNodeIndex !== -1) {
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
} else {
newData.unshift(_file);
}
+32 -22
View File
@@ -263,11 +263,13 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
status: SubscriptionStatus.running,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
status: SubscriptionStatus.running,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -298,12 +300,14 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
pid: null,
status: SubscriptionStatus.idle,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
pid: null,
status: SubscriptionStatus.idle,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -356,8 +360,10 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
message.success('删除成功');
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1);
setValue(result);
if (i !== -1) {
result.splice(i, 1);
setValue(result);
}
} else {
message.error(data);
}
@@ -397,11 +403,13 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
const newStatus = record.is_disabled === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
is_disabled: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
is_disabled: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -509,10 +517,12 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
const result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
result.splice(index, 1, {
...result[index],
status: SubscriptionStatus.idle,
});
if (index !== -1) {
result.splice(index, 1, {
...value[index],
status: SubscriptionStatus.idle,
});
}
}
setValue(result);
}
+4 -1
View File
@@ -195,7 +195,9 @@ const SubscriptionModal = ({
dependences,
branch,
extensions,
] = text.split(' ').map((x) => x.trim());
] = text
.split(' ')
.map((x) => x.trim().replace(/\"/g, '').replace(/\'/, ''));
form.setFieldsValue({
type:
type === 'raw'
@@ -209,6 +211,7 @@ const SubscriptionModal = ({
dependences,
branch,
extensions,
alias: formatAlias(url, branch),
});
}
}, []);
+6 -10
View File
@@ -1,12 +1,8 @@
export const version = '2.13.0';
export const version = '2.13.1';
export const changeLogLink = 'https://t.me/jiao_long/303';
export const changeLog = `2.13.0 版本说明
1. 新增订阅管理模块,支持公开仓库/私有仓库等
2. 支持修改头像
3. 修复特殊字符环境变量异常
4. 修改定时任务添加标签
5. 修复添加任务默认会展示一个空标签
6. 修复用户名显示
7. 修复依赖安装
8. 其他bug修复
export const changeLog = `2.13.1 版本说明
1. 修复新建订阅repo命令快捷导入
2. 修复执行订阅任务含有before/after命令时,造成面板无法访问
3. 修复任务详情日志列表访问
4. 修复安装依赖造成CPU满负荷,服务器崩溃
`;