perf: 通知标题优化

pull/273/head
xiaojunnuo 2024-12-11 11:30:32 +08:00
parent 0f051e322e
commit ff083ce684
11 changed files with 122 additions and 101 deletions

View File

@ -10,7 +10,8 @@ import { Decorator } from "../decorator/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService, IUrlService } from "../service/index.js"; import { ICnameProxyService, IEmailService, IPluginConfigService, IUrlService } from "../service/index.js";
import { FileStore } from "./file-store.js"; import { FileStore } from "./file-store.js";
import { cloneDeep, forEach, merge } from "lodash-es"; import { cloneDeep, forEach, merge } from "lodash-es";
import { INotificationService, NotificationBody, NotificationContext, notificationRegistry } from "../notification/index.js"; import { INotificationService } from "../notification/index.js";
export type SysInfo = { export type SysInfo = {
//系统标题 //系统标题
title?: string; title?: string;
@ -373,18 +374,17 @@ export class Executor {
let subject = ""; let subject = "";
let content = ""; let content = "";
const errorMessage = error?.message; const errorMessage = error?.message;
const sysTitle = this.options.sysInfo?.title || "Certd";
if (when === "start") { if (when === "start") {
subject = `${sysTitle}开始执行,${this.pipeline.title}${this.pipeline.id}`; subject = `开始执行,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`; content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "success") { } else if (when === "success") {
subject = `${sysTitle}执行成功,${this.pipeline.title}${this.pipeline.id}`; subject = `执行成功,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`; content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "turnToSuccess") { } else if (when === "turnToSuccess") {
subject = `${sysTitle}执行成功(失败转成功),${this.pipeline.title}${this.pipeline.id}`; subject = `执行成功(失败转成功),${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`; content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "error") { } else if (when === "error") {
subject = `${sysTitle}执行失败,${this.pipeline.title}${this.pipeline.id}`; subject = `执行失败,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}\n错误详情:${error.message}`; content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}\n错误详情:${error.message}`;
} else { } else {
return; return;
@ -407,43 +407,23 @@ export class Executor {
} }
} else { } else {
try { try {
//构建notification插件发送通知
let notifyConfig: any;
if (notification.notificationId === 0) {
notifyConfig = await this.options.notificationService.getDefault();
} else {
notifyConfig = await this.options.notificationService.getById(notification.notificationId);
}
if (notifyConfig == null) {
throw new Error(`通知配置<id:${notification.notificationId}>不存在`);
}
const notificationPlugin = notificationRegistry.get(notifyConfig.type);
const notificationCls: any = notificationPlugin.target;
const notificationSender = new notificationCls();
const notificationCtx: NotificationContext = {
http: utils.http,
logger,
utils,
emailService: this.options.emailService,
};
//设置参数
merge(notificationSender, notifyConfig.setting);
notificationSender.setCtx(notificationCtx);
await notificationSender.onInstance();
const body: NotificationBody = {
title: subject,
content,
userId: this.pipeline.userId,
pipeline: this.pipeline,
result: this.lastRuntime.pipeline.status,
pipelineId: this.pipeline.id,
historyId: this.runtime.id,
errorMessage,
url,
};
//发送通知 //发送通知
await notificationSender.send(body); await this.options.notificationService.send({
id: notification.notificationId,
useDefault: true,
useEmail: false,
body: {
title: subject,
content,
userId: this.pipeline.userId,
pipeline: this.pipeline,
result: this.lastRuntime.pipeline.status,
pipelineId: this.pipeline.id,
historyId: this.runtime.id,
errorMessage,
url,
},
});
} catch (e) { } catch (e) {
logger.error("send notification error", e); logger.error("send notification error", e);
} }

View File

@ -48,9 +48,18 @@ export type NotificationInstanceConfig = {
}; };
}; };
export type NotificationSendReq = {
id?: number;
useDefault?: boolean;
useEmail?: boolean;
emailAddress?: string;
logger?: ILogger;
body: NotificationBody;
};
export interface INotificationService { export interface INotificationService {
getById(id: number): Promise<NotificationInstanceConfig>; getById(id: number): Promise<NotificationInstanceConfig>;
getDefault(): Promise<NotificationInstanceConfig>; getDefault(): Promise<NotificationInstanceConfig>;
send(req: NotificationSendReq): Promise<void>;
} }
export interface INotification { export interface INotification {

View File

@ -1,9 +1,9 @@
// src/decorator/memoryCache.decorator.ts // src/decorator/memoryCache.decorator.ts
import { Decorator } from "../decorator/index.js"; import { Decorator } from "../decorator/index.js";
import * as _ from "lodash-es"; import * as _ from "lodash-es";
import { merge } from "lodash-es";
import { notificationRegistry } from "./registry.js"; import { notificationRegistry } from "./registry.js";
import { BaseNotification, NotificationBody, NotificationContext, NotificationDefine, NotificationInputDefine, NotificationInstanceConfig } from "./api.js"; import { BaseNotification, NotificationBody, NotificationContext, NotificationDefine, NotificationInputDefine, NotificationInstanceConfig } from "./api.js";
import { isPlus } from "@certd/plus-core";
// 提供一个唯一 key // 提供一个唯一 key
export const NOTIFICATION_CLASS_KEY = "pipeline:notification"; export const NOTIFICATION_CLASS_KEY = "pipeline:notification";
@ -47,9 +47,7 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
// @ts-ignore // @ts-ignore
const plugin = new register.target(); const plugin = new register.target();
for (const key in input) { merge(plugin, input);
plugin[key] = input[key];
}
if (!ctx) { if (!ctx) {
throw new Error("ctx is required"); throw new Error("ctx is required");
} }
@ -61,8 +59,5 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
export async function sendNotification(opts: { config: NotificationInstanceConfig; ctx: NotificationContext; body: NotificationBody }) { export async function sendNotification(opts: { config: NotificationInstanceConfig; ctx: NotificationContext; body: NotificationBody }) {
const notification: BaseNotification = await newNotification(opts.config.type, opts.config.setting, opts.ctx); const notification: BaseNotification = await newNotification(opts.config.type, opts.config.setting, opts.ctx);
if (notification.define.needPlus && !isPlus()) {
opts.body.content = `${opts.body.content}\n\n注意此通知渠道已调整为专业版功能后续版本将不再支持发送请尽快修改或升级为专业版`;
}
await notification.doSend(opts.body); await notification.doSend(opts.body);
} }

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IContext, NotificationBody, sendNotification, Step, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, IContext, NotificationBody, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { CertInfo } from "./acme.js"; import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js"; import { CertReader } from "./cert-reader.js";
@ -295,7 +295,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
* 35 * 35
* @param expires * @param expires
* @param maxDays * @param maxDays
* @returns {boolean}
*/ */
isWillExpire(expires: number, maxDays = 20) { isWillExpire(expires: number, maxDays = 20) {
if (expires == null) { if (expires == null) {
@ -312,39 +311,19 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
this.logger.info("发送证书申请成功通知"); this.logger.info("发送证书申请成功通知");
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id); const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
const body: NotificationBody = { const body: NotificationBody = {
title: `【Certd】证书申请成功【${this.pipeline.title}`, title: `证书申请成功【${this.pipeline.title}`,
content: `域名:${this.domains.join(",")}`, content: `域名:${this.domains.join(",")}`,
url: url, url: url,
}; };
try { try {
const defNotification = await this.ctx.notificationService.getDefault(); await this.ctx.notificationService.send({
if (defNotification) { useDefault: true,
this.logger.info(`通知渠道:${defNotification.name}`); useEmail: true,
const notificationCtx = { emailAddress: this.email,
http: this.ctx.http, body,
logger: this.logger, });
utils: this.ctx.utils,
emailService: this.ctx.emailService,
};
await sendNotification({
config: defNotification,
ctx: notificationCtx,
body,
});
return;
}
this.logger.warn("未配置默认通知,将发送邮件通知");
await this.sendSuccessEmail(body);
} catch (e) { } catch (e) {
this.logger.error("证书申请成功通知发送失败", e); this.logger.error("证书申请成功通知发送失败", e);
} }
} }
async sendSuccessEmail(body: NotificationBody) {
this.logger.info("发送邮件通知:" + this.email);
await this.ctx.emailService.send({
receivers: [this.email],
subject: body.title,
content: body.content,
});
}
} }

View File

@ -126,6 +126,7 @@ function clear() {
} }
async function emitValue(value: any) { async function emitValue(value: any) {
target.value = optionsDictRef.dataMap[value];
if (value !== 0 && pipeline?.value && target && pipeline.value.userId !== target.value.userId) { if (value !== 0 && pipeline?.value && target && pipeline.value.userId !== target.value.userId) {
message.error("对不起,您不能修改他人流水线的通知"); message.error("对不起,您不能修改他人流水线的通知");
return; return;

View File

@ -12,10 +12,7 @@ export class EmailController extends BaseController {
emailService: EmailService; emailService: EmailService;
@Post('/test', { summary: Constants.per.authOnly }) @Post('/test', { summary: Constants.per.authOnly })
public async test( public async test(@Body('receiver') receiver) {
@Body('receiver')
receiver
) {
const userId = super.getUserId(); const userId = super.getUserId();
await this.emailService.test(userId, receiver); await this.emailService.test(userId, receiver);
return this.ok({}); return this.ok({});

View File

@ -56,18 +56,6 @@ export class HandleController extends BaseController {
@Post('/notification', { summary: Constants.per.authOnly }) @Post('/notification', { summary: Constants.per.authOnly })
async notificationRequest(@Body(ALL) body: NotificationRequestHandleReq) { async notificationRequest(@Body(ALL) body: NotificationRequestHandleReq) {
const input = body.input.body; const input = body.input.body;
// if (body.input.id > 0) {
// const oldEntity = await this.notificationService.info(body.input.id);
// if (oldEntity) {
// if (oldEntity.userId !== this.getUserId()) {
// throw new Error('notification not found');
// }
// const param: any = {
// type: body.typeName,
// setting: JSON.stringify(body.input.access),
// };
// }
// }
const notification = await newNotification(body.typeName, input, { const notification = await newNotification(body.typeName, input, {
http, http,

View File

@ -1,12 +1,13 @@
import { Inject, Provide } from '@midwayjs/core'; import { Inject, Provide } from '@midwayjs/core';
import { cache, isDev, randomNumber } from '@certd/basic'; import { cache, isDev, randomNumber } from '@certd/basic';
import { SysSettingsService } from '@certd/lib-server'; import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { SmsServiceFactory } from '../sms/factory.js'; import { SmsServiceFactory } from '../sms/factory.js';
import { ISmsService } from '../sms/api.js'; import { ISmsService } from '../sms/api.js';
import { CodeErrorException } from '@certd/lib-server/dist/basic/exception/code-error-exception.js'; import { CodeErrorException } from '@certd/lib-server/dist/basic/exception/code-error-exception.js';
import { EmailService } from './email-service.js'; import { EmailService } from './email-service.js';
import { AccessService } from '../../pipeline/service/access-service.js'; import { AccessService } from '../../pipeline/service/access-service.js';
import { AccessSysGetter } from '../../pipeline/service/access-sys-getter.js'; import { AccessSysGetter } from '../../pipeline/service/access-sys-getter.js';
import { isComm } from '@certd/plus-core';
// {data: '<svg.../svg>', text: 'abcd'} // {data: '<svg.../svg>', text: 'abcd'}
/** /**
@ -99,9 +100,17 @@ export class CodeService {
throw new Error('randomStr不能为空'); throw new Error('randomStr不能为空');
} }
let siteTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
if (siteInfo) {
siteTitle = siteInfo.title || siteTitle;
}
}
const code = randomNumber(4); const code = randomNumber(4);
await this.emailService.send({ await this.emailService.send({
subject: '【Certd】验证码', subject: `${siteTitle}】验证码`,
content: `您的验证码是${code},请勿泄露`, content: `您的验证码是${code},请勿泄露`,
receivers: [email], receivers: [email],
}); });

View File

@ -91,7 +91,7 @@ export class EmailService implements IEmailService {
const mailOptions = { const mailOptions = {
from: `${sysTitle} <${emailConfig.sender}>`, from: `${sysTitle} <${emailConfig.sender}>`,
to: email.receivers.join(', '), // list of receivers to: email.receivers.join(', '), // list of receivers
subject: email.subject, subject: `${sysTitle}${email.subject}`,
text: email.content, text: email.content,
}; };
await transporter.sendMail(mailOptions); await transporter.sendMail(mailOptions);

View File

@ -1,4 +1,4 @@
import { INotificationService } from '@certd/pipeline'; import { INotificationService, NotificationSendReq } from '@certd/pipeline';
import { NotificationService } from './notification-service.js'; import { NotificationService } from './notification-service.js';
export class NotificationGetter implements INotificationService { export class NotificationGetter implements INotificationService {
@ -17,4 +17,8 @@ export class NotificationGetter implements INotificationService {
async getById(id: any) { async getById(id: any) {
return await this.notificationService.getById(id, this.userId); return await this.notificationService.getById(id, this.userId);
} }
async send(req: NotificationSendReq): Promise<void> {
return await this.notificationService.send(req, this.userId);
}
} }

View File

@ -1,9 +1,12 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService, ValidateException } from '@certd/lib-server'; import { BaseService, SysSettingsService, SysSiteInfo, ValidateException } from '@certd/lib-server';
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { NotificationEntity } from '../entity/notification.js'; import { NotificationEntity } from '../entity/notification.js';
import { NotificationInstanceConfig, notificationRegistry } from '@certd/pipeline'; import { NotificationInstanceConfig, notificationRegistry, NotificationSendReq, sendNotification } from '@certd/pipeline';
import { http, utils } from '@certd/basic';
import { EmailService } from '../../basic/service/email-service.js';
import { isComm } from '@certd/plus-core';
@Provide() @Provide()
@Scope(ScopeEnum.Singleton) @Scope(ScopeEnum.Singleton)
@ -11,6 +14,12 @@ export class NotificationService extends BaseService<NotificationEntity> {
@InjectEntityModel(NotificationEntity) @InjectEntityModel(NotificationEntity)
repository: Repository<NotificationEntity>; repository: Repository<NotificationEntity>;
@Inject()
emailService: EmailService;
@Inject()
sysSettingsService: SysSettingsService;
//@ts-ignore //@ts-ignore
getRepository() { getRepository() {
return this.repository; return this.repository;
@ -124,4 +133,54 @@ export class NotificationService extends BaseService<NotificationEntity> {
}); });
return this.buildNotificationInstanceConfig(res); return this.buildNotificationInstanceConfig(res);
} }
async send(req: NotificationSendReq, userId?: number) {
const logger = req.logger;
let notifyConfig: NotificationInstanceConfig = null;
if (req.id && req.id > 0) {
notifyConfig = await this.getById(req.id, userId);
if (!notifyConfig) {
logger.warn(`未找到通知配置<${req.id}>`);
}
}
if (!notifyConfig) {
if (req.id === 0 || req.useDefault) {
notifyConfig = await this.getDefault(userId);
if (!notifyConfig) {
logger.warn(`未找到默认通知配置`);
}
}
}
if (notifyConfig) {
//发送通知
logger.info('发送通知, 使用通知渠道:' + notifyConfig.name);
let siteTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
siteTitle = siteInfo?.title || siteTitle;
}
req.body.title = `${siteTitle}${req.body.title}`;
await sendNotification({
config: notifyConfig,
ctx: {
http: http,
logger: logger,
utils: utils,
emailService: this.emailService,
},
body: req.body,
});
} else {
if (req.useEmail && req.emailAddress) {
logger.info('使用邮件通知');
await this.emailService.send({
receivers: [req.emailAddress],
subject: req.body.title,
content: req.body.content,
});
}
}
}
} }