mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-22 22:36:06 +08:00
修改任务状态更新失败提示,重复运行提示
This commit is contained in:
parent
e5b35273f9
commit
51ef4e7476
|
@ -2,7 +2,7 @@ import { Router, Request, Response, NextFunction } from 'express';
|
|||
import { Container } from 'typedi';
|
||||
import { Logger } from 'winston';
|
||||
import config from '../config';
|
||||
import { getFileContentByName, readDirs, rmPath } from '../config/util';
|
||||
import { getFileContentByName, readDirs, removeAnsi, rmPath } from '../config/util';
|
||||
import { join, resolve } from 'path';
|
||||
import { celebrate, Joi } from 'celebrate';
|
||||
const route = Router();
|
||||
|
@ -42,7 +42,7 @@ export default (app: Router) => {
|
|||
return res.send({ code: 403, message: '暂无权限' });
|
||||
}
|
||||
const content = await getFileContentByName(finalPath);
|
||||
res.send({ code: 200, data: content });
|
||||
res.send({ code: 200, data: removeAnsi(content) });
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ const extraFile = path.join(configPath, 'extra.sh');
|
|||
const confBakDir = path.join(dataPath, 'config/bak/');
|
||||
const sampleFile = path.join(samplePath, 'config.sample.sh');
|
||||
const sqliteFile = path.join(samplePath, 'database.sqlite');
|
||||
const systemNotifyFile = path.join(preloadPath, 'system-notify.json');
|
||||
|
||||
const authError = '错误的用户名密码,请重试';
|
||||
const loginFaild = '请先登录!';
|
||||
|
@ -135,5 +134,4 @@ export default {
|
|||
sqliteFile,
|
||||
sshdPath,
|
||||
systemLogPath,
|
||||
systemNotifyFile,
|
||||
};
|
||||
|
|
|
@ -22,6 +22,10 @@ export async function getFileContentByName(fileName: string) {
|
|||
return '';
|
||||
}
|
||||
|
||||
export function removeAnsi(text: string) {
|
||||
return text.replace(/\x1b\[\d+m/g, '');
|
||||
}
|
||||
|
||||
export async function getLastModifyFilePath(dir: string) {
|
||||
let filePath = '';
|
||||
|
||||
|
@ -153,7 +157,12 @@ export function getPlatform(userAgent: string): 'mobile' | 'desktop' {
|
|||
let platform = 'desktop';
|
||||
if (system === 'windows' || system === 'macos' || system === 'linux') {
|
||||
platform = 'desktop';
|
||||
} else if (system === 'android' || system === 'ios' || system === 'openharmony' || testUa(/mobile/g)) {
|
||||
} else if (
|
||||
system === 'android' ||
|
||||
system === 'ios' ||
|
||||
system === 'openharmony' ||
|
||||
testUa(/mobile/g)
|
||||
) {
|
||||
platform = 'mobile';
|
||||
}
|
||||
|
||||
|
@ -233,14 +242,14 @@ interface IFile {
|
|||
key: string;
|
||||
type: 'directory' | 'file';
|
||||
parent: string;
|
||||
mtime: number;
|
||||
createTime: number;
|
||||
size?: number;
|
||||
children?: IFile[];
|
||||
}
|
||||
|
||||
export function dirSort(a: IFile, b: IFile): number {
|
||||
if (a.type === 'file' && b.type === 'file') {
|
||||
return b.mtime - a.mtime;
|
||||
return b.createTime - a.createTime;
|
||||
} else if (a.type === 'directory' && b.type === 'directory') {
|
||||
return a.title.localeCompare(b.title);
|
||||
} else {
|
||||
|
@ -274,7 +283,7 @@ export async function readDirs(
|
|||
key,
|
||||
type: 'directory',
|
||||
parent: relativePath,
|
||||
mtime: stats.mtime.getTime(),
|
||||
createTime: stats.birthtime.getTime(),
|
||||
children: children.sort(sort),
|
||||
});
|
||||
} else {
|
||||
|
@ -284,7 +293,7 @@ export async function readDirs(
|
|||
key,
|
||||
parent: relativePath,
|
||||
size: stats.size,
|
||||
mtime: stats.mtime.getTime(),
|
||||
createTime: stats.birthtime.getTime(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export default async () => {
|
|||
const [systemConfig] = await SystemModel.findOrCreate({
|
||||
where: { type: AuthDataType.systemConfig },
|
||||
});
|
||||
const [notifyConfig] = await SystemModel.findOrCreate({
|
||||
await SystemModel.findOrCreate({
|
||||
where: { type: AuthDataType.notification },
|
||||
});
|
||||
const [authConfig] = await SystemModel.findOrCreate({
|
||||
|
@ -68,11 +68,6 @@ export default async () => {
|
|||
});
|
||||
}
|
||||
|
||||
// 初始化通知配置
|
||||
if (notifyConfig.info) {
|
||||
await writeFile(config.systemNotifyFile, JSON.stringify(notifyConfig.info));
|
||||
}
|
||||
|
||||
const installDependencies = () => {
|
||||
// 初始化时安装所有处于安装中,安装成功,安装失败的依赖
|
||||
DependenceModel.findAll({
|
||||
|
|
|
@ -3,12 +3,9 @@ import got from 'got';
|
|||
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import winston from 'winston';
|
||||
import { parseBody, parseHeaders, safeJSONParse } from '../config/util';
|
||||
import { parseBody, parseHeaders } from '../config/util';
|
||||
import { NotificationInfo } from '../data/notify';
|
||||
import UserService from './user';
|
||||
import { readFile } from 'fs/promises';
|
||||
import config from '../config';
|
||||
|
||||
@Service()
|
||||
export default class NotificationService {
|
||||
|
@ -49,27 +46,6 @@ export default class NotificationService {
|
|||
|
||||
constructor() {}
|
||||
|
||||
public async externalNotify(
|
||||
title: string,
|
||||
content: string,
|
||||
): Promise<boolean | undefined> {
|
||||
const { type, ...rest } = safeJSONParse(
|
||||
await readFile(config.systemNotifyFile, 'utf-8'),
|
||||
);
|
||||
if (type) {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.params = rest;
|
||||
const notificationModeAction = this.modeMap.get(type);
|
||||
try {
|
||||
return await notificationModeAction?.call(this);
|
||||
} catch (error: any) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async notify(
|
||||
title: string,
|
||||
content: string,
|
||||
|
@ -154,15 +130,18 @@ export default class NotificationService {
|
|||
private async serverChan() {
|
||||
const { serverChanKey } = this.params;
|
||||
const matchResult = serverChanKey.match(/^sctp(\d+)t/i);
|
||||
const url = matchResult && matchResult[1]
|
||||
? `https://${matchResult[1]}.push.ft07.com/send/${serverChanKey}.send`
|
||||
: `https://sctapi.ftqq.com/${serverChanKey}.send`;
|
||||
const url =
|
||||
matchResult && matchResult[1]
|
||||
? `https://${matchResult[1]}.push.ft07.com/send/${serverChanKey}.send`
|
||||
: `https://sctapi.ftqq.com/${serverChanKey}.send`;
|
||||
|
||||
try {
|
||||
const res: any = await got
|
||||
.post(url, {
|
||||
...this.gotOption,
|
||||
body: `title=${encodeURIComponent(this.title)}&desp=${encodeURIComponent(this.content)}`,
|
||||
body: `title=${encodeURIComponent(
|
||||
this.title,
|
||||
)}&desp=${encodeURIComponent(this.content)}`,
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
})
|
||||
.json();
|
||||
|
@ -520,10 +499,18 @@ export default class NotificationService {
|
|||
} catch (error: any) {
|
||||
throw new Error(error.response ? error.response.body : error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async pushPlus() {
|
||||
const { pushPlusToken, pushPlusUser, pushplusWebhook, pushPlusTemplate, pushplusChannel, pushplusCallbackUrl, pushplusTo} = this.params;
|
||||
const {
|
||||
pushPlusToken,
|
||||
pushPlusUser,
|
||||
pushplusWebhook,
|
||||
pushPlusTemplate,
|
||||
pushplusChannel,
|
||||
pushplusCallbackUrl,
|
||||
pushplusTo,
|
||||
} = this.params;
|
||||
const url = `https://www.pushplus.plus/send`;
|
||||
try {
|
||||
let body = {
|
||||
|
@ -537,13 +524,11 @@ export default class NotificationService {
|
|||
channel: `${pushplusChannel || 'wechat'}`,
|
||||
webhook: `${pushplusWebhook || ''}`,
|
||||
callbackUrl: `${pushplusCallbackUrl || ''}`,
|
||||
to: `${pushplusTo || ''}`
|
||||
to: `${pushplusTo || ''}`,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const res: any = await got
|
||||
.post(url, body)
|
||||
.json();
|
||||
const res: any = await got.post(url, body).json();
|
||||
|
||||
if (res.code === 200) {
|
||||
return true;
|
||||
|
@ -678,37 +663,46 @@ export default class NotificationService {
|
|||
const encodeRfc2047 = (text: string, charset: string = 'UTF-8'): string => {
|
||||
const encodedText = Buffer.from(text).toString('base64');
|
||||
return `=?${charset}?B?${encodedText}?=`;
|
||||
};
|
||||
};
|
||||
try {
|
||||
const encodedTitle = encodeRfc2047(this.title);
|
||||
const res: any = await got
|
||||
.post(`${ntfyUrl || 'https://ntfy.sh'}/${ntfyTopic}`, {
|
||||
...this.gotOption,
|
||||
body: `${this.content}`,
|
||||
headers: { 'Title': encodedTitle, 'Priority': `${ntfyPriority || '3'}` },
|
||||
});
|
||||
if (res.statusCode === 200) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(JSON.stringify(res));
|
||||
}
|
||||
const encodedTitle = encodeRfc2047(this.title);
|
||||
const res: any = await got.post(
|
||||
`${ntfyUrl || 'https://ntfy.sh'}/${ntfyTopic}`,
|
||||
{
|
||||
...this.gotOption,
|
||||
body: `${this.content}`,
|
||||
headers: { Title: encodedTitle, Priority: `${ntfyPriority || '3'}` },
|
||||
},
|
||||
);
|
||||
if (res.statusCode === 200) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(JSON.stringify(res));
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.response ? error.response.body : error);
|
||||
throw new Error(error.response ? error.response.body : error);
|
||||
}
|
||||
}
|
||||
|
||||
private async wxPusherBot() {
|
||||
const { wxPusherBotAppToken, wxPusherBotTopicIds, wxPusherBotUids } = this.params;
|
||||
const { wxPusherBotAppToken, wxPusherBotTopicIds, wxPusherBotUids } =
|
||||
this.params;
|
||||
// 处理 topicIds,将分号分隔的字符串转为数组
|
||||
const topicIds = wxPusherBotTopicIds ? wxPusherBotTopicIds.split(';')
|
||||
.map(id => id.trim())
|
||||
.filter(id => id)
|
||||
.map(id => parseInt(id)) : [];
|
||||
const topicIds = wxPusherBotTopicIds
|
||||
? wxPusherBotTopicIds
|
||||
.split(';')
|
||||
.map((id) => id.trim())
|
||||
.filter((id) => id)
|
||||
.map((id) => parseInt(id))
|
||||
: [];
|
||||
|
||||
// 处理 uids,将分号分隔的字符串转为数组
|
||||
const uids = wxPusherBotUids ? wxPusherBotUids.split(';')
|
||||
.map(uid => uid.trim())
|
||||
.filter(uid => uid) : [];
|
||||
const uids = wxPusherBotUids
|
||||
? wxPusherBotUids
|
||||
.split(';')
|
||||
.map((uid) => uid.trim())
|
||||
.filter((uid) => uid)
|
||||
: [];
|
||||
|
||||
// topic_ids 和 uids 至少要有一个
|
||||
if (!topicIds.length && !uids.length) {
|
||||
|
@ -727,7 +721,7 @@ export default class NotificationService {
|
|||
contentType: 2,
|
||||
topicIds: topicIds,
|
||||
uids: uids,
|
||||
verifyPayType: 0
|
||||
verifyPayType: 0,
|
||||
},
|
||||
})
|
||||
.json();
|
||||
|
@ -742,7 +736,6 @@ export default class NotificationService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private async chronocat() {
|
||||
const { chronocatURL, chronocatQQ, chronocatToken } = this.params;
|
||||
try {
|
||||
|
|
|
@ -440,7 +440,7 @@ export default class SystemService {
|
|||
const logs = result
|
||||
.reverse()
|
||||
.filter((x) => x.title.endsWith('.log'))
|
||||
.filter((x) => x.mtime >= startTime && x.mtime <= endTime);
|
||||
.filter((x) => x.createTime >= startTime && x.createTime <= endTime);
|
||||
|
||||
res.set({
|
||||
'Content-Length': sum(logs.map((x) => x.size)),
|
||||
|
|
|
@ -11,6 +11,9 @@ import {
|
|||
IScheduleFn,
|
||||
TCron,
|
||||
} from './interface';
|
||||
import config from '../config';
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { ApiClient } from '../protos/api';
|
||||
|
||||
class TaskLimit {
|
||||
private dependenyLimit = new PQueue({ concurrency: 1 });
|
||||
|
@ -33,6 +36,11 @@ class TaskLimit {
|
|||
private systemLimit = new PQueue({
|
||||
concurrency: Math.max(os.cpus().length, 4),
|
||||
});
|
||||
private client = new ApiClient(
|
||||
`0.0.0.0:${config.cronPort}`,
|
||||
credentials.createInsecure(),
|
||||
{ 'grpc.enable_http_proxy': 0 },
|
||||
);
|
||||
|
||||
get cronLimitActiveCount() {
|
||||
return this.cronLimit.pending;
|
||||
|
@ -126,9 +134,18 @@ class TaskLimit {
|
|||
if (result?.length > 5) {
|
||||
if (repeatTimes < 3) {
|
||||
this.repeatCronNotifyMap.set(cron.id, repeatTimes + 1);
|
||||
this.notificationService.externalNotify(
|
||||
'任务重复运行',
|
||||
`任务:${cron.name},命令:${cron.command},定时:${cron.schedule},处于运行中的超过 5 个,请检查定时设置`,
|
||||
this.client.systemNotify(
|
||||
{
|
||||
title: '任务重复运行',
|
||||
content: `任务:${cron.name},命令:${cron.command},定时:${cron.schedule},处于运行中的超过 5 个,请检查定时设置`,
|
||||
},
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
Logger.error(
|
||||
`[schedule][任务重复运行] 通知失败 ${JSON.stringify(err)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
Logger.warn(`[schedule][任务重复运行] 参数 ${JSON.stringify(cron)}`);
|
||||
|
|
|
@ -178,7 +178,10 @@ update_cron() {
|
|||
code=$(echo "$api" | jq -r .code)
|
||||
message=$(echo "$api" | jq -r .message)
|
||||
if [[ $code != 200 ]]; then
|
||||
echo -e "\n## 更新任务状态失败(${message})\n"
|
||||
if [[ ! $message ]]; then
|
||||
message="$api"
|
||||
fi
|
||||
echo -e "${message}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const grpc = require('@grpc/grpc-js');
|
||||
const protoLoader = require('@grpc/proto-loader');
|
||||
const path = require('path');
|
||||
|
||||
const PROTO_PATH = path.resolve(__dirname, '../../back/protos/api.proto');
|
||||
console.log(process.env.QL_DIR);
|
||||
const PROTO_PATH = `${process.env.QL_DIR}/back/protos/api.proto`;
|
||||
const options = {
|
||||
keepCase: true,
|
||||
longs: String,
|
||||
|
|
|
@ -438,8 +438,14 @@ clear_env() {
|
|||
}
|
||||
|
||||
handle_task_start() {
|
||||
[[ $ID ]] && update_cron "\"$ID\"" "0" "$$" "$log_path" "$begin_timestamp"
|
||||
echo -e "## 开始执行... $begin_time\n"
|
||||
local error_message=""
|
||||
if [[ $ID ]]; then
|
||||
local error=$(update_cron "\"$ID\"" "0" "$$" "$log_path" "$begin_timestamp")
|
||||
if [[ $error ]]; then
|
||||
error_message=", 任务状态更新失败(${error})"
|
||||
fi
|
||||
fi
|
||||
echo -e "## 开始执行... ${begin_time}${error_message}\n"
|
||||
}
|
||||
|
||||
run_task_before() {
|
||||
|
@ -472,8 +478,13 @@ handle_task_end() {
|
|||
|
||||
[[ "$diff_time" == 0 ]] && diff_time=1
|
||||
|
||||
echo -e "\n## 执行结束$suffix... $end_time 耗时 $diff_time 秒 "
|
||||
[[ $ID ]] && update_cron "\"$ID\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time"
|
||||
if [[ $ID ]]; then
|
||||
local error=$(update_cron "\"$ID\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time")
|
||||
if [[ $error ]]; then
|
||||
error_message=", 任务状态更新失败(${error})"
|
||||
fi
|
||||
fi
|
||||
echo -e "\n## 执行结束$suffix... $end_time 耗时 $diff_time 秒${error_message} "
|
||||
}
|
||||
|
||||
init_env
|
||||
|
|
|
@ -489,7 +489,6 @@ main() {
|
|||
local time_format="%Y-%m-%d %H:%M:%S"
|
||||
local time=$(date "+$time_format")
|
||||
local begin_timestamp=$(format_timestamp "$time_format" "$time")
|
||||
[[ $ID ]] && update_cron "\"$ID\"" "0" "$$" "$log_path" "$begin_timestamp"
|
||||
|
||||
local begin_time=$(format_time "$time_format" "$time")
|
||||
|
||||
|
@ -497,6 +496,8 @@ main() {
|
|||
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
|
||||
fi
|
||||
|
||||
[[ $ID ]] && update_cron "\"$ID\"" "0" "$$" "$log_path" "$begin_timestamp"
|
||||
|
||||
case $p1 in
|
||||
update)
|
||||
fix_config
|
||||
|
|
Loading…
Reference in New Issue
Block a user