mirror of
https://github.com/certd/certd.git
synced 2026-04-24 12:27:25 +08:00
perf: 支持企业微信群聊机器人通知
This commit is contained in:
@@ -3,13 +3,14 @@ import { RunHistory, RunnableCollection } from "./run-history.js";
|
||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js";
|
||||
import { ContextFactory, IContext } from "./context.js";
|
||||
import { IStorage } from "./storage.js";
|
||||
import { createAxiosService, hashUtils, HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
|
||||
import { createAxiosService, hashUtils, HttpRequestConfig, ILogger, logger, utils } from "@Certd/basic";
|
||||
import { IAccessService } from "../access/index.js";
|
||||
import { RegistryItem } from "../registry/index.js";
|
||||
import { Decorator } from "../decorator/index.js";
|
||||
import { ICnameProxyService, IEmailService, IPluginConfigService } from "../service/index.js";
|
||||
import { ICnameProxyService, IEmailService, IPluginConfigService, IUrlService } from "../service/index.js";
|
||||
import { FileStore } from "./file-store.js";
|
||||
import { cloneDeep, forEach, merge } from "lodash-es";
|
||||
import { INotificationService, NotificationBody, NotificationContext, notificationRegistry } from "../notification/index.js";
|
||||
|
||||
export type ExecutorOptions = {
|
||||
pipeline: Pipeline;
|
||||
@@ -17,10 +18,13 @@ export type ExecutorOptions = {
|
||||
onChanged: (history: RunHistory) => Promise<void>;
|
||||
accessService: IAccessService;
|
||||
emailService: IEmailService;
|
||||
notificationService: INotificationService;
|
||||
cnameProxyService: ICnameProxyService;
|
||||
pluginConfigService: IPluginConfigService;
|
||||
urlService: IUrlService;
|
||||
fileRootDir?: string;
|
||||
user: UserInfo;
|
||||
baseURL?: string;
|
||||
};
|
||||
|
||||
export class Executor {
|
||||
@@ -357,20 +361,22 @@ export class Executor {
|
||||
if (!this.pipeline.notifications) {
|
||||
return;
|
||||
}
|
||||
const url = await this.options.urlService.getPipelineDetailUrl(this.pipeline.id, this.runtime.id);
|
||||
let subject = "";
|
||||
let content = "";
|
||||
const errorMessage = error?.message;
|
||||
if (when === "start") {
|
||||
subject = `【CertD】开始执行,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}`;
|
||||
subject = `【Certd】开始执行,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
|
||||
} else if (when === "success") {
|
||||
subject = `【CertD】执行成功,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}`;
|
||||
subject = `【Certd】执行成功,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
|
||||
} else if (when === "turnToSuccess") {
|
||||
subject = `【CertD】执行成功(错误转成功),【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}`;
|
||||
subject = `【Certd】执行成功(失败转成功),【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
|
||||
} else if (when === "error") {
|
||||
subject = `【CertD】执行失败,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}\nerror:${error.message}`;
|
||||
subject = `【Certd】执行失败,【${this.pipeline.id}】${this.pipeline.title}`;
|
||||
content = `buildId:${this.runtime.id}\n查看详情:${url}\nerror:${error.message}`;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -390,6 +396,39 @@ export class Executor {
|
||||
} catch (e) {
|
||||
logger.error("send email error", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
//构建notification插件,发送通知
|
||||
const notifyConfig = await this.options.notificationService.getById(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);
|
||||
} catch (e) {
|
||||
logger.error("send notification error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function NewRunHistory(obj: any) {
|
||||
return history;
|
||||
}
|
||||
export class RunHistory {
|
||||
id!: string;
|
||||
id!: any;
|
||||
pipeline!: Pipeline;
|
||||
logs: {
|
||||
[runnableId: string]: string[];
|
||||
|
||||
@@ -62,7 +62,7 @@ export type FileItem = {
|
||||
path: string;
|
||||
};
|
||||
export type Runnable = {
|
||||
id: string;
|
||||
id: any;
|
||||
title: string;
|
||||
strategy?: RunnableStrategy;
|
||||
runnableType?: string; // pipeline, stage, task , step
|
||||
@@ -83,6 +83,9 @@ export type Notification = {
|
||||
type: NotificationType;
|
||||
when: NotificationWhen[];
|
||||
options: EmailOptions;
|
||||
notificationId: number;
|
||||
title: string;
|
||||
subType: string;
|
||||
};
|
||||
|
||||
export type Pipeline = Runnable & {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { PluginRequestHandleReq } from "../plugin";
|
||||
import { Registrable } from "../registry/index.js";
|
||||
import { FormItemProps } from "../dt/index.js";
|
||||
import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js";
|
||||
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||
import * as _ from "lodash-es";
|
||||
import { IEmailService } from "../service";
|
||||
import { IEmailService } from "../service/index.js";
|
||||
|
||||
export type NotificationBody = {
|
||||
userId: number;
|
||||
title: string;
|
||||
content: string;
|
||||
pipeline: Pipeline;
|
||||
pipelineId: number;
|
||||
result?: HistoryResult;
|
||||
historyId: number;
|
||||
url: string;
|
||||
extra?: any;
|
||||
options?: any;
|
||||
errorMessage?: string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
export type NotificationRequestHandleReqInput<T = any> = {
|
||||
@@ -34,11 +35,21 @@ export type NotificationDefine = Registrable & {
|
||||
[key: string]: NotificationInputDefine;
|
||||
};
|
||||
};
|
||||
|
||||
export type NotificationInstanceConfig = {
|
||||
id: number;
|
||||
type: string;
|
||||
userId: number;
|
||||
setting: {
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
|
||||
export interface INotificationService {
|
||||
send(body: NotificationBody): Promise<void>;
|
||||
getById(id: number): Promise<NotificationInstanceConfig>;
|
||||
}
|
||||
|
||||
export interface INotification extends INotificationService {
|
||||
export interface INotification {
|
||||
ctx: NotificationContext;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -55,6 +66,9 @@ export abstract class BaseNotification implements INotification {
|
||||
http!: HttpClient;
|
||||
logger!: ILogger;
|
||||
abstract send(body: NotificationBody): Promise<void>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
async onInstance() {}
|
||||
setCtx(ctx: NotificationContext) {
|
||||
this.ctx = ctx;
|
||||
this.http = ctx.http;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { Decorator } from "../decorator/index.js";
|
||||
import * as _ from "lodash-es";
|
||||
import { notificationRegistry } from "./registry.js";
|
||||
import { http, logger, utils } from "@certd/basic";
|
||||
import { NotificationContext, NotificationDefine, NotificationInputDefine } from "./api.js";
|
||||
|
||||
// 提供一个唯一 key
|
||||
@@ -39,7 +38,7 @@ export function NotificationInput(input?: NotificationInputDefine): PropertyDeco
|
||||
};
|
||||
}
|
||||
|
||||
export function newNotification(type: string, input: any, ctx?: NotificationContext) {
|
||||
export function newNotification(type: string, input: any, ctx: NotificationContext) {
|
||||
const register = notificationRegistry.get(type);
|
||||
if (register == null) {
|
||||
throw new Error(`notification ${type} not found`);
|
||||
@@ -50,11 +49,7 @@ export function newNotification(type: string, input: any, ctx?: NotificationCont
|
||||
access[key] = input[key];
|
||||
}
|
||||
if (!ctx) {
|
||||
ctx = {
|
||||
http,
|
||||
logger,
|
||||
utils,
|
||||
};
|
||||
throw new Error("ctx is required");
|
||||
}
|
||||
access.ctx = ctx;
|
||||
return access;
|
||||
|
||||
@@ -19,7 +19,7 @@ export type OnRegisterContext<T> = {
|
||||
value: RegistryItem<T>;
|
||||
};
|
||||
export type OnRegister<T> = (ctx: OnRegisterContext<T>) => void;
|
||||
export class Registry<T> {
|
||||
export class Registry<T = any> {
|
||||
type = "";
|
||||
storage: {
|
||||
[key: string]: RegistryItem<T>;
|
||||
@@ -89,7 +89,7 @@ export class Registry<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export function createRegistry<T>(type: string, onRegister?: OnRegister<T>) {
|
||||
export function createRegistry<T>(type: string, onRegister?: OnRegister<T>): Registry<T> {
|
||||
const pipelineregistrycacheKey = "PIPELINE_REGISTRY_CACHE";
|
||||
// @ts-ignore
|
||||
let cached: any = global[pipelineregistrycacheKey];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./email.js";
|
||||
export * from "./cname.js";
|
||||
export * from "./config.js";
|
||||
export * from "./url.js";
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface IUrlService {
|
||||
getPipelineDetailUrl(pipelineId: number, historyId: number): Promise<string>;
|
||||
}
|
||||
Reference in New Issue
Block a user