mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-23 14:56:07 +08:00
重构任务执行逻辑
This commit is contained in:
parent
bb47d67d0b
commit
9dcc547ac7
|
@ -232,6 +232,14 @@ export async function fileExist(file: any) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createFile(file: string, data: string = '') {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||||
|
fs.writeFileSync(file, data);
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function concurrentRun(
|
export async function concurrentRun(
|
||||||
fnList: Array<() => Promise<any>> = [],
|
fnList: Array<() => Promise<any>> = [],
|
||||||
max = 5,
|
max = 5,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import winston from 'winston';
|
import winston from 'winston';
|
||||||
import nodeSchedule from 'node-schedule';
|
import nodeSchedule from 'node-schedule';
|
||||||
import { exec } from 'child_process';
|
import { ChildProcessWithoutNullStreams, exec, spawn } from 'child_process';
|
||||||
import {
|
import {
|
||||||
ToadScheduler,
|
ToadScheduler,
|
||||||
LongIntervalJob,
|
LongIntervalJob,
|
||||||
AsyncTask,
|
AsyncTask,
|
||||||
SimpleIntervalSchedule,
|
SimpleIntervalSchedule,
|
||||||
} from 'toad-scheduler';
|
} from 'toad-scheduler';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface ScheduleTaskType {
|
interface ScheduleTaskType {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -16,6 +17,19 @@ interface ScheduleTaskType {
|
||||||
schedule?: string;
|
schedule?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TaskCallbacks {
|
||||||
|
onStart?: (
|
||||||
|
cp: ChildProcessWithoutNullStreams,
|
||||||
|
startTime: dayjs.Dayjs,
|
||||||
|
) => void;
|
||||||
|
onEnd?: (
|
||||||
|
cp: ChildProcessWithoutNullStreams,
|
||||||
|
endTime: dayjs.Dayjs,
|
||||||
|
diff: number,
|
||||||
|
) => void;
|
||||||
|
onError?: (message: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ScheduleService {
|
export default class ScheduleService {
|
||||||
private scheduleStacks = new Map<string, nodeSchedule.Job>();
|
private scheduleStacks = new Map<string, nodeSchedule.Job>();
|
||||||
|
@ -26,12 +40,63 @@ export default class ScheduleService {
|
||||||
|
|
||||||
constructor(@Inject('logger') private logger: winston.Logger) {}
|
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||||
|
|
||||||
async createCronTask({
|
async runTask(command: string, callbacks: TaskCallbacks = {}) {
|
||||||
id = 0,
|
return new Promise(async (resolve, reject) => {
|
||||||
command,
|
try {
|
||||||
name,
|
const startTime = dayjs();
|
||||||
schedule = '',
|
const cp = spawn(command, { shell: '/bin/bash' });
|
||||||
}: ScheduleTaskType) {
|
|
||||||
|
callbacks.onStart?.(cp, startTime);
|
||||||
|
|
||||||
|
cp.stderr.on('data', (data) => {
|
||||||
|
this.logger.error(
|
||||||
|
'执行任务%s失败,时间:%s, 错误信息:%j',
|
||||||
|
command,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
data.toString(),
|
||||||
|
);
|
||||||
|
callbacks.onError?.(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.on('error', (err) => {
|
||||||
|
this.logger.error(
|
||||||
|
'执行任务%s失败,时间:%s, 错误信息:%j',
|
||||||
|
command,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
callbacks.onError?.(JSON.stringify(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.on('exit', async (code, signal) => {
|
||||||
|
this.logger.info(
|
||||||
|
`${command} pid: ${cp.pid} exit ${code} signal ${signal}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.on('close', async (code) => {
|
||||||
|
const endTime = dayjs();
|
||||||
|
this.logger.info(`${command} pid: ${cp.pid} closed ${code}`);
|
||||||
|
callbacks.onEnd?.(cp, endTime, endTime.diff(startTime, 'seconds'));
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await this.logger.error(
|
||||||
|
'执行任务%s失败,时间:%s, 错误信息:%j',
|
||||||
|
command,
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
callbacks.onError?.(JSON.stringify(error));
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCronTask(
|
||||||
|
{ id = 0, command, name, schedule = '' }: ScheduleTaskType,
|
||||||
|
callbacks?: TaskCallbacks,
|
||||||
|
) {
|
||||||
const _id = this.formatId(id);
|
const _id = this.formatId(id);
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'[创建cron任务],任务ID: %s,cron: %s,任务名: %s,执行命令: %s',
|
'[创建cron任务],任务ID: %s,cron: %s,任务名: %s,执行命令: %s',
|
||||||
|
@ -44,39 +109,7 @@ export default class ScheduleService {
|
||||||
this.scheduleStacks.set(
|
this.scheduleStacks.set(
|
||||||
_id,
|
_id,
|
||||||
nodeSchedule.scheduleJob(_id, schedule, async () => {
|
nodeSchedule.scheduleJob(_id, schedule, async () => {
|
||||||
try {
|
await this.runTask(command, callbacks);
|
||||||
exec(
|
|
||||||
command,
|
|
||||||
{ maxBuffer: this.maxBuffer },
|
|
||||||
async (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
await this.logger.error(
|
|
||||||
'执行任务%s失败,时间:%s, 错误信息:%j',
|
|
||||||
command,
|
|
||||||
new Date().toLocaleString(),
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stderr) {
|
|
||||||
await this.logger.error(
|
|
||||||
'执行任务%s失败,时间:%s, 错误信息:%j',
|
|
||||||
command,
|
|
||||||
new Date().toLocaleString(),
|
|
||||||
stderr,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
await this.logger.error(
|
|
||||||
'执行任务%s失败,时间:%s, 错误信息:%j',
|
|
||||||
command,
|
|
||||||
new Date().toLocaleString(),
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +124,7 @@ export default class ScheduleService {
|
||||||
{ id = 0, command, name = '' }: ScheduleTaskType,
|
{ id = 0, command, name = '' }: ScheduleTaskType,
|
||||||
schedule: SimpleIntervalSchedule,
|
schedule: SimpleIntervalSchedule,
|
||||||
runImmediately = true,
|
runImmediately = true,
|
||||||
|
callbacks?: TaskCallbacks,
|
||||||
) {
|
) {
|
||||||
const _id = this.formatId(id);
|
const _id = this.formatId(id);
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
|
@ -103,34 +137,7 @@ export default class ScheduleService {
|
||||||
name,
|
name,
|
||||||
async () => {
|
async () => {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
await this.runTask(command, callbacks);
|
||||||
exec(
|
|
||||||
command,
|
|
||||||
{ maxBuffer: this.maxBuffer },
|
|
||||||
async (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
await this.logger.error(
|
|
||||||
'执行任务%s失败,时间:%s, 错误信息:%j',
|
|
||||||
command,
|
|
||||||
new Date().toLocaleString(),
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stderr) {
|
|
||||||
await this.logger.error(
|
|
||||||
'执行任务%s失败,时间:%s, 错误信息:%j',
|
|
||||||
command,
|
|
||||||
new Date().toLocaleString(),
|
|
||||||
stderr,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
|
|
|
@ -6,15 +6,25 @@ import {
|
||||||
SubscriptionModel,
|
SubscriptionModel,
|
||||||
SubscriptionStatus,
|
SubscriptionStatus,
|
||||||
} from '../data/subscription';
|
} from '../data/subscription';
|
||||||
import { exec, execSync, spawn } from 'child_process';
|
import {
|
||||||
|
ChildProcessWithoutNullStreams,
|
||||||
|
exec,
|
||||||
|
execSync,
|
||||||
|
spawn,
|
||||||
|
} from 'child_process';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cron_parser from 'cron-parser';
|
import cron_parser from 'cron-parser';
|
||||||
import { getFileContentByName, concurrentRun, fileExist } from '../config/util';
|
import {
|
||||||
|
getFileContentByName,
|
||||||
|
concurrentRun,
|
||||||
|
fileExist,
|
||||||
|
createFile,
|
||||||
|
} from '../config/util';
|
||||||
import { promises, existsSync } from 'fs';
|
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 ScheduleService from './schedule';
|
import ScheduleService, { TaskCallbacks } from './schedule';
|
||||||
import { SimpleIntervalSchedule } from 'toad-scheduler';
|
import { SimpleIntervalSchedule } from 'toad-scheduler';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
|
@ -97,7 +107,11 @@ export default class SubscriptionService {
|
||||||
doc.command = this.formatCommand(doc);
|
doc.command = this.formatCommand(doc);
|
||||||
if (doc.schedule_type === 'crontab') {
|
if (doc.schedule_type === 'crontab') {
|
||||||
this.scheduleService.cancelCronTask(doc as any);
|
this.scheduleService.cancelCronTask(doc as any);
|
||||||
needCreate && this.scheduleService.createCronTask(doc as any);
|
needCreate &&
|
||||||
|
this.scheduleService.createCronTask(
|
||||||
|
doc as any,
|
||||||
|
this.taskCallbacks(doc),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.scheduleService.cancelIntervalTask(doc as any);
|
this.scheduleService.cancelIntervalTask(doc as any);
|
||||||
const { type, value } = doc.interval_schedule as any;
|
const { type, value } = doc.interval_schedule as any;
|
||||||
|
@ -105,10 +119,64 @@ export default class SubscriptionService {
|
||||||
this.scheduleService.createIntervalTask(
|
this.scheduleService.createIntervalTask(
|
||||||
doc as any,
|
doc as any,
|
||||||
{ [type]: value } as SimpleIntervalSchedule,
|
{ [type]: value } as SimpleIntervalSchedule,
|
||||||
|
true,
|
||||||
|
this.taskCallbacks(doc),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleLogPath(
|
||||||
|
logPath: string,
|
||||||
|
data: string = '',
|
||||||
|
): Promise<string> {
|
||||||
|
const absolutePath = path.resolve(config.logPath, logPath);
|
||||||
|
const logFileExist = await fileExist(absolutePath);
|
||||||
|
if (!logFileExist) {
|
||||||
|
await createFile(absolutePath, data);
|
||||||
|
}
|
||||||
|
return absolutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private taskCallbacks(doc: Subscription): TaskCallbacks {
|
||||||
|
return {
|
||||||
|
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
|
||||||
|
const logTime = startTime.format('YYYY-MM-DD-HH-mm-ss');
|
||||||
|
const logPath = `${doc.alias}/${logTime}.log`;
|
||||||
|
await this.handleLogPath(
|
||||||
|
logPath as string,
|
||||||
|
`## 开始执行... ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n`,
|
||||||
|
);
|
||||||
|
await SubscriptionModel.update(
|
||||||
|
{
|
||||||
|
status: SubscriptionStatus.running,
|
||||||
|
pid: cp.pid,
|
||||||
|
log_path: logPath,
|
||||||
|
},
|
||||||
|
{ where: { id: doc.id } },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onEnd: async (cp, endTime, diff) => {
|
||||||
|
const sub = await this.getDb({ id: doc.id });
|
||||||
|
await SubscriptionModel.update(
|
||||||
|
{ status: SubscriptionStatus.idle, pid: undefined },
|
||||||
|
{ where: { id: doc.id } },
|
||||||
|
);
|
||||||
|
const absolutePath = await this.handleLogPath(sub.log_path as string);
|
||||||
|
fs.appendFileSync(
|
||||||
|
absolutePath,
|
||||||
|
`\n## 执行结束... ${endTime.format(
|
||||||
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
|
)} 耗时 ${diff} 秒`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: async (message: string) => {
|
||||||
|
const sub = await this.getDb({ id: doc.id });
|
||||||
|
const absolutePath = await this.handleLogPath(sub.log_path as string);
|
||||||
|
fs.appendFileSync(absolutePath, `\n${message}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async create(payload: Subscription): Promise<Subscription> {
|
public async create(payload: Subscription): Promise<Subscription> {
|
||||||
const tab = new Subscription(payload);
|
const tab = new Subscription(payload);
|
||||||
const doc = await this.insert(tab);
|
const doc = await this.insert(tab);
|
||||||
|
@ -195,17 +263,14 @@ export default class SubscriptionService {
|
||||||
this.handleTask(doc, false);
|
this.handleTask(doc, false);
|
||||||
const command = this.formatCommand(doc);
|
const command = this.formatCommand(doc);
|
||||||
const err = await this.killTask(command);
|
const err = await this.killTask(command);
|
||||||
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
|
const absolutePath = await this.handleLogPath(doc.log_path as string);
|
||||||
const logFileExist = doc.log_path && (await fileExist(absolutePath));
|
const str = err ? `\n${err}` : '';
|
||||||
if (logFileExist) {
|
fs.appendFileSync(
|
||||||
const str = err ? `\n${err}` : '';
|
`${absolutePath}`,
|
||||||
fs.appendFileSync(
|
`${str}\n## 执行结束... ${new Date()
|
||||||
`${absolutePath}`,
|
.toLocaleString('zh', { hour12: false })
|
||||||
`${str}\n## 执行结束... ${new Date()
|
.replace(' 24:', ' 00:')} `,
|
||||||
.toLocaleString('zh', { hour12: false })
|
);
|
||||||
.replace(' 24:', ' 00:')} `,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await SubscriptionModel.update(
|
await SubscriptionModel.update(
|
||||||
|
@ -249,57 +314,18 @@ export default class SubscriptionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runSingle(cronId: number): Promise<number> {
|
private async runSingle(subscriptionId: number) {
|
||||||
return new Promise(async (resolve: any) => {
|
const subscription = await this.getDb({ id: subscriptionId });
|
||||||
const cron = await this.getDb({ id: cronId });
|
if (subscription.status !== SubscriptionStatus.queued) {
|
||||||
if (cron.status !== SubscriptionStatus.queued) {
|
return;
|
||||||
resolve();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { id, log_path, name } = cron;
|
const command = this.formatCommand(subscription);
|
||||||
const command = this.formatCommand(cron);
|
|
||||||
const absolutePath = path.resolve(config.logPath, `${log_path}`);
|
|
||||||
const logFileExist = log_path && (await fileExist(absolutePath));
|
|
||||||
|
|
||||||
this.logger.silly('Running job' + name);
|
await this.scheduleService.runTask(
|
||||||
this.logger.silly('ID: ' + id);
|
command,
|
||||||
this.logger.silly('Original command: ' + command);
|
this.taskCallbacks(subscription),
|
||||||
|
);
|
||||||
const cp = spawn(command, { shell: '/bin/bash' });
|
|
||||||
|
|
||||||
await SubscriptionModel.update(
|
|
||||||
{ status: SubscriptionStatus.running, pid: cp.pid },
|
|
||||||
{ where: { id } },
|
|
||||||
);
|
|
||||||
cp.stderr.on('data', (data) => {
|
|
||||||
if (logFileExist) {
|
|
||||||
fs.appendFileSync(`${absolutePath}`, `${data}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cp.on('error', (err) => {
|
|
||||||
if (logFileExist) {
|
|
||||||
fs.appendFileSync(`${absolutePath}`, `${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cp.on('exit', async (code, signal) => {
|
|
||||||
this.logger.info(`${''} pid: ${cp.pid} exit ${code} signal ${signal}`);
|
|
||||||
await SubscriptionModel.update(
|
|
||||||
{ status: SubscriptionStatus.idle, pid: undefined },
|
|
||||||
{ where: { id } },
|
|
||||||
);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
cp.on('close', async (code) => {
|
|
||||||
this.logger.info(`${''} pid: ${cp.pid} closed ${code}`);
|
|
||||||
await SubscriptionModel.update(
|
|
||||||
{ status: SubscriptionStatus.idle, pid: undefined },
|
|
||||||
{ where: { id } },
|
|
||||||
);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async disabled(ids: number[]) {
|
public async disabled(ids: number[]) {
|
||||||
|
@ -324,11 +350,8 @@ export default class SubscriptionService {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
|
const absolutePath = await this.handleLogPath(doc.log_path as string);
|
||||||
const logFileExist = doc.log_path && (await fileExist(absolutePath));
|
return getFileContentByName(absolutePath);
|
||||||
if (logFileExist) {
|
|
||||||
return getFileContentByName(`${absolutePath}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async logs(id: number) {
|
public async logs(id: number) {
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cron-parser": "^4.2.1",
|
"cron-parser": "^4.2.1",
|
||||||
|
"dayjs": "^1.11.2",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-jwt": "^6.1.1",
|
"express-jwt": "^6.1.1",
|
||||||
|
|
|
@ -478,11 +478,8 @@ main() {
|
||||||
repo)
|
repo)
|
||||||
get_user_info
|
get_user_info
|
||||||
get_uniq_path "$p2" "$p6"
|
get_uniq_path "$p2" "$p6"
|
||||||
log_path="$dir_log/update/${log_time}_${uniq_path}.log"
|
|
||||||
echo -e "## 开始执行... $begin_time\n" >>$log_path
|
|
||||||
[[ -f $task_error_log_path ]] && cat $task_error_log_path >>$log_path
|
|
||||||
if [[ -n $p2 ]]; then
|
if [[ -n $p2 ]]; then
|
||||||
update_repo "$p2" "$p3" "$p4" "$p5" "$p6" >>$log_path
|
update_repo "$p2" "$p3" "$p4" "$p5" "$p6"
|
||||||
else
|
else
|
||||||
echo -e "命令输入错误...\n"
|
echo -e "命令输入错误...\n"
|
||||||
usage
|
usage
|
||||||
|
@ -491,11 +488,8 @@ main() {
|
||||||
raw)
|
raw)
|
||||||
get_user_info
|
get_user_info
|
||||||
get_uniq_path "$p2"
|
get_uniq_path "$p2"
|
||||||
log_path="$dir_log/update/${log_time}_${uniq_path}.log"
|
|
||||||
echo -e "## 开始执行... $begin_time\n" >>$log_path
|
|
||||||
[[ -f $task_error_log_path ]] && cat $task_error_log_path >>$log_path
|
|
||||||
if [[ -n $p2 ]]; then
|
if [[ -n $p2 ]]; then
|
||||||
update_raw "$p2" >>$log_path
|
update_raw "$p2"
|
||||||
else
|
else
|
||||||
echo -e "命令输入错误...\n"
|
echo -e "命令输入错误...\n"
|
||||||
usage
|
usage
|
||||||
|
|
|
@ -477,6 +477,13 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
: 'subscription';
|
: 'subscription';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (logSubscription) {
|
||||||
|
localStorage.setItem('logSubscription', logSubscription.id);
|
||||||
|
setIsLogModalVisible(true);
|
||||||
|
}
|
||||||
|
}, [logSubscription]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getSubscriptions();
|
getSubscriptions();
|
||||||
}, [searchText]);
|
}, [searchText]);
|
||||||
|
@ -541,7 +548,7 @@ const Subscription = ({ headerStyle, isPhone, theme }: any) => {
|
||||||
handleCancel={() => {
|
handleCancel={() => {
|
||||||
setIsLogModalVisible(false);
|
setIsLogModalVisible(false);
|
||||||
}}
|
}}
|
||||||
cron={logSubscription}
|
subscription={logSubscription}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,22 +8,14 @@ import {
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { PageLoading } from '@ant-design/pro-layout';
|
import { PageLoading } from '@ant-design/pro-layout';
|
||||||
|
|
||||||
enum CrontabStatus {
|
|
||||||
'running',
|
|
||||||
'idle',
|
|
||||||
'disabled',
|
|
||||||
'queued',
|
|
||||||
}
|
|
||||||
const { Countdown } = Statistic;
|
|
||||||
|
|
||||||
const SubscriptionLogModal = ({
|
const SubscriptionLogModal = ({
|
||||||
cron,
|
subscription,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
visible,
|
visible,
|
||||||
data,
|
data,
|
||||||
logUrl,
|
logUrl,
|
||||||
}: {
|
}: {
|
||||||
cron?: any;
|
subscription?: any;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
handleCancel: () => void;
|
handleCancel: () => void;
|
||||||
data?: string;
|
data?: string;
|
||||||
|
@ -33,49 +25,29 @@ const SubscriptionLogModal = ({
|
||||||
const [loading, setLoading] = useState<any>(true);
|
const [loading, setLoading] = useState<any>(true);
|
||||||
const [executing, setExecuting] = useState<any>(true);
|
const [executing, setExecuting] = useState<any>(true);
|
||||||
const [isPhone, setIsPhone] = useState(false);
|
const [isPhone, setIsPhone] = useState(false);
|
||||||
const [theme, setTheme] = useState<string>('');
|
|
||||||
|
|
||||||
const getCronLog = (isFirst?: boolean) => {
|
const getCronLog = (isFirst?: boolean) => {
|
||||||
if (isFirst) {
|
if (isFirst) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
}
|
}
|
||||||
request
|
request
|
||||||
.get(logUrl ? logUrl : `${config.apiPrefix}crons/${cron.id}/log`)
|
.get(
|
||||||
|
logUrl
|
||||||
|
? logUrl
|
||||||
|
: `${config.apiPrefix}subscriptions/${subscription.id}/log`,
|
||||||
|
)
|
||||||
.then((data: any) => {
|
.then((data: any) => {
|
||||||
if (localStorage.getItem('logCron') === String(cron.id)) {
|
if (
|
||||||
|
localStorage.getItem('logSubscription') === String(subscription.id)
|
||||||
|
) {
|
||||||
const log = data.data as string;
|
const log = data.data as string;
|
||||||
setValue(log || '暂无日志');
|
setValue(log || '暂无日志');
|
||||||
setExecuting(
|
setExecuting(log && !log.includes('执行结束'));
|
||||||
log && !log.includes('执行结束') && !log.includes('重启面板'),
|
if (log && !log.includes('执行结束')) {
|
||||||
);
|
|
||||||
if (log && !log.includes('执行结束') && !log.includes('重启面板')) {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getCronLog();
|
getCronLog();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
log &&
|
|
||||||
log.includes('重启面板') &&
|
|
||||||
cron.status === CrontabStatus.running
|
|
||||||
) {
|
|
||||||
message.warning({
|
|
||||||
content: (
|
|
||||||
<span>
|
|
||||||
系统将在
|
|
||||||
<Countdown
|
|
||||||
className="inline-countdown"
|
|
||||||
format="ss"
|
|
||||||
value={Date.now() + 1000 * 30}
|
|
||||||
/>
|
|
||||||
秒后自动刷新
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
duration: 10,
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload();
|
|
||||||
}, 30000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
@ -86,7 +58,7 @@ const SubscriptionLogModal = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
localStorage.removeItem('logCron');
|
localStorage.removeItem('logSubscription');
|
||||||
handleCancel();
|
handleCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,16 +67,18 @@ const SubscriptionLogModal = ({
|
||||||
<>
|
<>
|
||||||
{(executing || loading) && <Loading3QuartersOutlined spin />}
|
{(executing || loading) && <Loading3QuartersOutlined spin />}
|
||||||
{!executing && !loading && <CheckCircleOutlined />}
|
{!executing && !loading && <CheckCircleOutlined />}
|
||||||
<span style={{ marginLeft: 5 }}>日志-{cron && cron.name}</span>{' '}
|
<span style={{ marginLeft: 5 }}>
|
||||||
|
日志-{subscription && subscription.name}
|
||||||
|
</span>{' '}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (cron && cron.id && visible) {
|
if (subscription && subscription.id && visible) {
|
||||||
getCronLog(true);
|
getCronLog(true);
|
||||||
}
|
}
|
||||||
}, [cron, visible]);
|
}, [subscription, visible]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user