mirror of
https://github.com/whyour/qinglong.git
synced 2026-06-30 20:35:09 +08:00
修改退出进程逻辑
This commit is contained in:
+5
-5
@@ -170,7 +170,7 @@ export default (app: Router) => {
|
||||
body: Joi.object({
|
||||
filename: Joi.string().required(),
|
||||
path: Joi.string().allow(''),
|
||||
type: Joi.string().optional()
|
||||
type: Joi.string().optional(),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
@@ -183,7 +183,7 @@ export default (app: Router) => {
|
||||
};
|
||||
const filePath = join(config.scriptPath, path, filename);
|
||||
if (type === 'directory') {
|
||||
emptyDir(filePath);
|
||||
emptyDir(filePath);
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
@@ -255,19 +255,19 @@ export default (app: Router) => {
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
filename: Joi.string().required(),
|
||||
content: Joi.string().optional().allow(''),
|
||||
path: Joi.string().optional().allow(''),
|
||||
pid: Joi.number().optional().allow(''),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
let { filename, content, path } = req.body;
|
||||
let { filename, path, pid } = req.body;
|
||||
const { name, ext } = parse(filename);
|
||||
const filePath = join(config.scriptPath, path, `${name}.swap${ext}`);
|
||||
|
||||
const scriptService = Container.get(ScriptService);
|
||||
const result = await scriptService.stopScript(filePath);
|
||||
const result = await scriptService.stopScript(filePath, pid);
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
|
||||
+33
-4
@@ -4,6 +4,8 @@ import got from 'got';
|
||||
import iconv from 'iconv-lite';
|
||||
import { exec } from 'child_process';
|
||||
import FormData from 'form-data';
|
||||
import psTreeFun from 'pstree.remy';
|
||||
import { promisify } from 'util';
|
||||
|
||||
export function getFileContentByName(fileName: string) {
|
||||
if (fs.existsSync(fileName)) {
|
||||
@@ -287,15 +289,15 @@ enum FileType {
|
||||
interface IFile {
|
||||
title: string;
|
||||
key: string;
|
||||
type: 'directory' | 'file',
|
||||
type: 'directory' | 'file';
|
||||
parent: string;
|
||||
mtime: number;
|
||||
children?: IFile[],
|
||||
children?: IFile[];
|
||||
}
|
||||
|
||||
export function dirSort(a: IFile, b: IFile) {
|
||||
if (a.type !== b.type) return FileType[a.type] < FileType[b.type] ? -1 : 1
|
||||
else if (a.mtime !== b.mtime) return a.mtime > b.mtime ? -1 : 1
|
||||
if (a.type !== b.type) return FileType[a.type] < FileType[b.type] ? -1 : 1;
|
||||
else if (a.mtime !== b.mtime) return a.mtime > b.mtime ? -1 : 1;
|
||||
}
|
||||
|
||||
export function readDirs(
|
||||
@@ -452,3 +454,30 @@ export function parseBody(
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function psTree(pid: number): Promise<number[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
psTreeFun(pid, (err: any, pids: number[]) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(pids.filter((x) => !isNaN(x)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function killTask(pid: number): Promise<number[]> {
|
||||
const pids = await psTree(pid);
|
||||
if (pids.length) {
|
||||
process.kill(pids[0], 2);
|
||||
} else {
|
||||
process.kill(pid, 2);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPid(name: string) {
|
||||
let taskCommand = `ps -ef | grep "${name}" | grep -v grep | awk '{print $1}'`;
|
||||
const execAsync = promisify(exec);
|
||||
let pid = (await execAsync(taskCommand)).stdout;
|
||||
return Number(pid);
|
||||
}
|
||||
|
||||
+7
-60
@@ -5,13 +5,16 @@ import { Crontab, CrontabModel, CrontabStatus } from '../data/cron';
|
||||
import { exec, execSync, spawn } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import cron_parser from 'cron-parser';
|
||||
import { getFileContentByName, concurrentRun, fileExist } from '../config/util';
|
||||
import {
|
||||
getFileContentByName,
|
||||
concurrentRun,
|
||||
fileExist,
|
||||
killTask,
|
||||
} from '../config/util';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { Op } from 'sequelize';
|
||||
import path from 'path';
|
||||
import dayjs from 'dayjs';
|
||||
import { LOG_END_SYMBOL } from '../config/const';
|
||||
|
||||
@Service()
|
||||
export default class CronService {
|
||||
@@ -315,31 +318,11 @@ export default class CronService {
|
||||
for (const doc of docs) {
|
||||
if (doc.pid) {
|
||||
try {
|
||||
process.kill(-doc.pid);
|
||||
await killTask(doc.pid);
|
||||
} catch (error) {
|
||||
this.logger.silly(error);
|
||||
}
|
||||
}
|
||||
const err = await this.killTask(doc.command);
|
||||
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
|
||||
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) {
|
||||
const str = err ? `\n${err}` : '';
|
||||
fs.appendFileSync(
|
||||
`${absolutePath}`,
|
||||
`${str}\n## 执行结束... ${endTime.format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)}${diffTimeStr}${LOG_END_SYMBOL}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await CrontabModel.update(
|
||||
@@ -348,42 +331,6 @@ export default class CronService {
|
||||
);
|
||||
}
|
||||
|
||||
public async killTask(name: string) {
|
||||
let taskCommand = `ps -ef | grep "${name}" | grep -v grep | awk '{print $1}'`;
|
||||
const execAsync = promisify(exec);
|
||||
try {
|
||||
let pid = (await execAsync(taskCommand)).stdout;
|
||||
if (pid) {
|
||||
pid = (await execAsync(`pstree -p ${pid}`)).stdout;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let pids = pid.match(/\(\d+/g);
|
||||
const killLogs = [];
|
||||
if (pids && pids.length > 0) {
|
||||
// node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个
|
||||
pids = pids.slice(0, 3);
|
||||
for (const id of pids) {
|
||||
const c = `kill -9 ${id.slice(1)}`;
|
||||
try {
|
||||
const { stdout, stderr } = await execAsync(c);
|
||||
if (stderr) {
|
||||
killLogs.push(stderr);
|
||||
}
|
||||
if (stdout) {
|
||||
killLogs.push(stdout);
|
||||
}
|
||||
} catch (error: any) {
|
||||
killLogs.push(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return killLogs.length > 0 ? JSON.stringify(killLogs) : '';
|
||||
} catch (e) {
|
||||
return JSON.stringify(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async runSingle(cronId: number): Promise<number> {
|
||||
return new Promise(async (resolve: any) => {
|
||||
const cron = await this.getDb({ id: cronId });
|
||||
|
||||
@@ -44,12 +44,11 @@ export default class ScheduleService {
|
||||
|
||||
async runTask(command: string, callbacks: TaskCallbacks = {}) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const startTime = dayjs();
|
||||
await callbacks.onBefore?.(startTime);
|
||||
|
||||
const cp = spawn(command, { shell: '/bin/bash' });
|
||||
try {
|
||||
const startTime = dayjs();
|
||||
await callbacks.onBefore?.(startTime);
|
||||
|
||||
const cp = spawn(command, { shell: '/bin/bash' });
|
||||
|
||||
// TODO:
|
||||
callbacks.onStart?.(cp, startTime);
|
||||
|
||||
@@ -100,8 +99,8 @@ export default class ScheduleService {
|
||||
error,
|
||||
);
|
||||
await callbacks.onError?.(JSON.stringify(error));
|
||||
resolve(null);
|
||||
}
|
||||
resolve(cp.pid);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -7,6 +7,7 @@ import CronService from './cron';
|
||||
import ScheduleService, { TaskCallbacks } from './schedule';
|
||||
import config from '../config';
|
||||
import { LOG_END_SYMBOL } from '../config/const';
|
||||
import { getPid, killTask } from '../config/util';
|
||||
|
||||
@Service()
|
||||
export default class ScriptService {
|
||||
@@ -42,16 +43,24 @@ export default class ScriptService {
|
||||
public async runScript(filePath: string) {
|
||||
const relativePath = path.relative(config.scriptPath, filePath);
|
||||
const command = `task -l ${relativePath} now`;
|
||||
this.scheduleService.runTask(command, this.taskCallbacks(filePath));
|
||||
const pid = this.scheduleService.runTask(
|
||||
command,
|
||||
this.taskCallbacks(filePath),
|
||||
);
|
||||
|
||||
return { code: 200 };
|
||||
return { code: 200, data: pid };
|
||||
}
|
||||
|
||||
public async stopScript(filePath: string) {
|
||||
const relativePath = path.relative(config.scriptPath, filePath);
|
||||
const err = await this.cronService.killTask(`task -l ${relativePath} now`);
|
||||
public async stopScript(filePath: string, pid: number) {
|
||||
let str = '';
|
||||
if (!pid) {
|
||||
const relativePath = path.relative(config.scriptPath, filePath);
|
||||
pid = await getPid(`task -l ${relativePath} now`);
|
||||
}
|
||||
try {
|
||||
await killTask(pid);
|
||||
} catch (error) {}
|
||||
|
||||
const str = err ? `\n${err}` : '';
|
||||
this.sockService.sendMessage({
|
||||
type: 'manuallyRunScript',
|
||||
message: `${str}\n## 执行结束... ${new Date()
|
||||
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
concurrentRun,
|
||||
fileExist,
|
||||
createFile,
|
||||
killTask,
|
||||
} from '../config/util';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { Op } from 'sequelize';
|
||||
import path from 'path';
|
||||
import ScheduleService, { TaskCallbacks } from './schedule';
|
||||
@@ -351,19 +351,16 @@ export default class SubscriptionService {
|
||||
for (const doc of docs) {
|
||||
if (doc.pid) {
|
||||
try {
|
||||
process.kill(-doc.pid);
|
||||
await killTask(doc.pid);
|
||||
} catch (error) {
|
||||
this.logger.silly(error);
|
||||
}
|
||||
}
|
||||
const command = this.formatCommand(doc);
|
||||
const err = await this.killTask(command);
|
||||
const absolutePath = await this.handleLogPath(doc.log_path as string);
|
||||
const str = err ? `\n${err}` : '';
|
||||
|
||||
fs.appendFileSync(
|
||||
`${absolutePath}`,
|
||||
`${str}\n## 执行结束... ${dayjs().format(
|
||||
`\n## 执行结束... ${dayjs().format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)}${LOG_END_SYMBOL}`,
|
||||
);
|
||||
@@ -375,41 +372,6 @@ export default class SubscriptionService {
|
||||
);
|
||||
}
|
||||
|
||||
public async killTask(name: string) {
|
||||
let taskCommand = `ps -ef | grep "${name}" | grep -v grep | awk '{print $1}'`;
|
||||
const execAsync = promisify(exec);
|
||||
try {
|
||||
let pid = (await execAsync(taskCommand)).stdout;
|
||||
if (pid) {
|
||||
pid = (await execAsync(`pstree -p ${pid}`)).stdout;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let pids = pid.match(/\(\d+/g);
|
||||
const killLogs = [];
|
||||
if (pids && pids.length > 0) {
|
||||
// node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个
|
||||
for (const id of pids) {
|
||||
const c = `kill -9 ${id.slice(1)}`;
|
||||
try {
|
||||
const { stdout, stderr } = await execAsync(c);
|
||||
if (stderr) {
|
||||
killLogs.push(stderr);
|
||||
}
|
||||
if (stdout) {
|
||||
killLogs.push(stdout);
|
||||
}
|
||||
} catch (error: any) {
|
||||
killLogs.push(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return killLogs.length > 0 ? JSON.stringify(killLogs) : '';
|
||||
} catch (e) {
|
||||
return JSON.stringify(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async runSingle(subscriptionId: number) {
|
||||
const subscription = await this.getDb({ id: subscriptionId });
|
||||
if (subscription.status !== SubscriptionStatus.queued) {
|
||||
|
||||
Reference in New Issue
Block a user