修改任务状态更新失败提示,重复运行提示

This commit is contained in:
whyour 2025-01-12 00:19:14 +08:00
parent e5b35273f9
commit 51ef4e7476
11 changed files with 115 additions and 88 deletions

View File

@ -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);
}

View File

@ -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,
};

View File

@ -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(),
});
}
}

View File

@ -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({

View File

@ -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 {

View File

@ -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)),

View File

@ -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)}`);

View File

@ -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
}

View File

@ -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,

View File

@ -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

View File

@ -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