From 51ef4e747620394ec5a9af16c36d8a96e560470c Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 12 Jan 2025 00:19:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=BB=E5=8A=A1=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9B=B4=E6=96=B0=E5=A4=B1=E8=B4=A5=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E5=A4=8D=E8=BF=90=E8=A1=8C=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/log.ts | 4 +- back/config/index.ts | 2 - back/config/util.ts | 19 +++++-- back/loaders/initData.ts | 7 +-- back/services/notify.ts | 115 ++++++++++++++++++--------------------- back/services/system.ts | 2 +- back/shared/pLimit.ts | 23 +++++++- shell/api.sh | 5 +- shell/preload/client.js | 4 +- shell/share.sh | 19 +++++-- shell/update.sh | 3 +- 11 files changed, 115 insertions(+), 88 deletions(-) diff --git a/back/api/log.ts b/back/api/log.ts index 2ddc32be..0d2068fb 100644 --- a/back/api/log.ts +++ b/back/api/log.ts @@ -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); } diff --git a/back/config/index.ts b/back/config/index.ts index 9b4d7037..be155ef5 100644 --- a/back/config/index.ts +++ b/back/config/index.ts @@ -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, }; diff --git a/back/config/util.ts b/back/config/util.ts index a6cc5367..17536f6d 100644 --- a/back/config/util.ts +++ b/back/config/util.ts @@ -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(), }); } } diff --git a/back/loaders/initData.ts b/back/loaders/initData.ts index 0320e409..96b4552e 100644 --- a/back/loaders/initData.ts +++ b/back/loaders/initData.ts @@ -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({ diff --git a/back/services/notify.ts b/back/services/notify.ts index 17ea4586..781a17ba 100644 --- a/back/services/notify.ts +++ b/back/services/notify.ts @@ -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 { - 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) : []; + // 处理 uids,将分号分隔的字符串转为数组 + 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 { diff --git a/back/services/system.ts b/back/services/system.ts index afd2555b..047d5622 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -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)), diff --git a/back/shared/pLimit.ts b/back/shared/pLimit.ts index af0dd7c6..2221ca7c 100644 --- a/back/shared/pLimit.ts +++ b/back/shared/pLimit.ts @@ -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)}`); diff --git a/shell/api.sh b/shell/api.sh index 844e963c..4d5b5c25 100755 --- a/shell/api.sh +++ b/shell/api.sh @@ -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 } diff --git a/shell/preload/client.js b/shell/preload/client.js index 021a39b4..2f6adcab 100644 --- a/shell/preload/client.js +++ b/shell/preload/client.js @@ -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, diff --git a/shell/share.sh b/shell/share.sh index 93994f20..342788d5 100755 --- a/shell/share.sh +++ b/shell/share.sh @@ -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 diff --git a/shell/update.sh b/shell/update.sh index 834b411a..f8f067b1 100755 --- a/shell/update.sh +++ b/shell/update.sh @@ -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