diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts
index ffa38dc6..11de4eef 100644
--- a/packages/core/pipeline/src/core/executor.ts
+++ b/packages/core/pipeline/src/core/executor.ts
@@ -367,16 +367,16 @@ export class Executor {
const errorMessage = error?.message;
if (when === "start") {
subject = `【Certd】开始执行,【${this.pipeline.id}】${this.pipeline.title}`;
- content = `buildId:${this.runtime.id}\n查看详情:${url}`;
+ content = `buildId:${this.runtime.id}`;
} else if (when === "success") {
subject = `【Certd】执行成功,【${this.pipeline.id}】${this.pipeline.title}`;
- content = `buildId:${this.runtime.id}\n查看详情:${url}`;
+ content = `buildId:${this.runtime.id}`;
} else if (when === "turnToSuccess") {
subject = `【Certd】执行成功(失败转成功),【${this.pipeline.id}】${this.pipeline.title}`;
- content = `buildId:${this.runtime.id}\n查看详情:${url}`;
+ content = `buildId:${this.runtime.id}`;
} else if (when === "error") {
subject = `【Certd】执行失败,【${this.pipeline.id}】${this.pipeline.title}`;
- content = `buildId:${this.runtime.id}\n查看详情:${url}\nerror:${error.message}`;
+ content = `buildId:${this.runtime.id}\nerror:${error.message}`;
} else {
return;
}
diff --git a/packages/core/pipeline/src/notification/api.ts b/packages/core/pipeline/src/notification/api.ts
index 7920c505..10ffbb33 100644
--- a/packages/core/pipeline/src/notification/api.ts
+++ b/packages/core/pipeline/src/notification/api.ts
@@ -98,14 +98,14 @@ export abstract class BaseNotification implements INotification {
await this.send({
userId: 0,
title: "【Certd】测试通知",
- content: "测试通知\n\n查看详情:http://www.baidu.com",
+ content: "测试通知",
pipeline: {
id: 1,
title: "测试流水线",
} as any,
pipelineId: 1,
historyId: 1,
- url: "http://www.baidu.com",
+ url: "https://certd.docmirror.cn",
});
}
}
diff --git a/packages/ui/certd-client/src/components/vip-button/index.vue b/packages/ui/certd-client/src/components/vip-button/index.vue
index aa8ef823..96809169 100644
--- a/packages/ui/certd-client/src/components/vip-button/index.vue
+++ b/packages/ui/certd-client/src/components/vip-button/index.vue
@@ -199,11 +199,13 @@ function openUpgrade() {
const vipTypeDefine = {
free: {
title: "基础版",
+ desc: "免费使用",
type: "free",
privilege: ["证书申请功能无限制", "证书流水线数量10条", "常用的主机、cdn等部署插件"]
},
plus: {
title: "专业版",
+ desc: "功能增强,适用于个人企业内部使用",
type: "plus",
privilege: ["可加VIP群,需求优先实现", "证书流水线数量无限制", "免配置发邮件功能", "支持宝塔、易盾、群晖、1Panel、cdnfly等部署插件"],
trial: {
@@ -215,6 +217,7 @@ function openUpgrade() {
},
comm: {
title: "商业版",
+ desc: "商业授权,可对外运营",
type: "comm",
privilege: ["拥有专业版所有特权", "允许商用,可修改logo、标题", "数据统计", "插件管理", "多用户无限制", "支持用户支付(敬请期待)"]
}
@@ -268,6 +271,7 @@ function openUpgrade() {
)}
+
{item.desc}
{item.privilege.map((p: string) => (
-
@@ -333,7 +337,7 @@ function openUpgrade() {
padding: 10px;
border: 1px solid #eee;
border-radius: 5px;
- height: 170px;
+ height: 195px;
//background-color: rgba(250, 237, 167, 0.79);
&.current {
border-color: green;
diff --git a/packages/ui/certd-client/src/style/common.less b/packages/ui/certd-client/src/style/common.less
index 3420cedf..ad936aed 100644
--- a/packages/ui/certd-client/src/style/common.less
+++ b/packages/ui/certd-client/src/style/common.less
@@ -77,6 +77,12 @@ h1, h2, h3, h4, h5, h6 {
display: flex;
flex-direction: column;
}
+.align-left{
+ text-align: left;
+}
+.align-right{
+ text-align: right;
+}
.scroll-y {
overflow-y: auto;
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
index 6ff6ae47..32e86d2c 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
@@ -180,8 +180,10 @@
-
- 【通知】 {{ item.title || item.type }}
+
+
+ 【通知】 {{ item.title || item.type }}
+
@@ -198,8 +200,10 @@
-
- 【通知】 {{ item.title || item.type }}
+
+
+ 【通知】 {{ item.title || item.type }}
+
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/anpush/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/anpush/index.ts
index dbc8cc6e..e934d5f0 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/anpush/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/anpush/index.ts
@@ -39,7 +39,7 @@ export class AnPushNotification extends BaseNotification {
},
data: {
title: body.title,
- content: body.content,
+ content: body.content + '[查看详情](' + body.url + ')',
channel: this.channel,
},
};
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/bark/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/bark/index.ts
new file mode 100644
index 00000000..47a0dc9d
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/bark/index.ts
@@ -0,0 +1,52 @@
+/**
+ * curl -X "POST" "https://api.day.app/your_key" \
+ * -H 'Content-Type: application/json; charset=utf-8' \
+ * -d $'{
+ * "body": "Test Bark Server",
+ * "title": "Test Title",
+ * "badge": 1,
+ * "category": "myNotificationCategory",
+ * "sound": "minuet.caf",
+ * "icon": "https://day.app/assets/images/avatar.jpg",
+ * "group": "test",
+ * "url": "https://mritd.com"
+ * }'
+ */
+
+import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
+
+@IsNotification({
+ name: 'bark',
+ title: 'Bark 通知',
+ desc: 'Bark 推送通知插件',
+})
+export class BarkNotification extends BaseNotification {
+ @NotificationInput({
+ title: '服务地址',
+ component: {
+ placeholder: 'https://api.day.app/your_key',
+ },
+ required: true,
+ helper: '你的bark服务地址+key',
+ })
+ webhook = '';
+ async send(body: NotificationBody) {
+ if (!this.webhook) {
+ throw new Error('服务器地址不能为空');
+ }
+
+ const payload = {
+ body: body.content, // 使用传入的内容或默认内容
+ title: body.title, // 使用传入的标题或默认标题
+ };
+
+ await this.http.request({
+ url: `${this.webhook}`,
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ data: payload,
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/discord/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/discord/index.ts
new file mode 100644
index 00000000..fb60e0b8
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/discord/index.ts
@@ -0,0 +1,53 @@
+import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
+
+@IsNotification({
+ name: 'discord',
+ title: 'Discord 通知',
+ desc: 'Discord 机器人通知',
+})
+export class DiscordNotification extends BaseNotification {
+ @NotificationInput({
+ title: 'Webhook URL',
+ component: {
+ placeholder: 'https://discord.com/api/webhooks/xxxxx/xxxx',
+ },
+ helper: '[Discord Webhook 说明](https://discord.com/developers/docs/resources/webhook#execute-webhook)',
+ required: true,
+ })
+ webhook = '';
+
+ @NotificationInput({
+ title: '提醒指定成员',
+ component: {
+ name: 'a-select',
+ vModel: 'value',
+ mode: 'tags',
+ open: false,
+ },
+ required: false,
+ helper: '填写成员的Id,或者角色Id(&id),或者everyone',
+ })
+ mentionedList!: string[];
+
+ async send(body: NotificationBody) {
+ if (!this.webhook) {
+ throw new Error('Webhook URL 不能为空');
+ }
+
+ // 创建 Discord 消息体
+ let content = `${body.title}\n${body.content}\n[查看详情](${body.url})`;
+ if (this.mentionedList && this.mentionedList.length > 0) {
+ content += `\n${this.mentionedList.map(item => `<@${item}> `).join('')}`;
+ }
+
+ const json = {
+ content: content,
+ };
+
+ await this.http.request({
+ url: this.webhook,
+ method: 'POST',
+ data: json,
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/email/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/email/index.ts
index ee9478b0..8bab072b 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/email/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/email/index.ts
@@ -23,7 +23,7 @@ export class EmailNotification extends BaseNotification {
await this.ctx.emailService.send({
userId: body.userId,
subject: body.title,
- content: body.content,
+ content: body.content + '\n[查看详情](' + body.url + ')',
receivers: this.receivers,
});
}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/index.ts
index 510fe10e..ab4a047a 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/index.ts
@@ -4,3 +4,7 @@ export * from './iyuu/index.js';
export * from './webhook/index.js';
export * from './serverchan/index.js';
export * from './anpush/index.js';
+export * from './telegram/index.js';
+export * from './discord/index.js';
+export * from './slack/index.js';
+export * from './bark/index.js';
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/iyuu/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/iyuu/index.ts
index f7dbe2d3..b4899854 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/iyuu/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/iyuu/index.ts
@@ -25,7 +25,7 @@ export class IyuuNotification extends BaseNotification {
method: 'POST',
data: {
text: body.title,
- desp: body.content,
+ desp: body.content + '[查看详情](' + body.url + ')',
},
});
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/serverchan/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/serverchan/index.ts
index d23855c1..7be7485d 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/serverchan/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/serverchan/index.ts
@@ -52,7 +52,7 @@ export class ServerChanNotification extends BaseNotification {
method: 'POST',
data: {
text: body.title,
- desp: body.content,
+ desp: body.content + '[查看详情](' + body.url + ')',
},
});
}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/slack/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/slack/index.ts
new file mode 100644
index 00000000..cdb260ca
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/slack/index.ts
@@ -0,0 +1,32 @@
+import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
+
+@IsNotification({
+ name: 'slack',
+ title: 'Slack通知',
+ desc: 'Slack消息推送通知',
+})
+export class SlackNotification extends BaseNotification {
+ @NotificationInput({
+ title: 'webhook地址',
+ component: {
+ placeholder: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX',
+ },
+ helper: '[APPS](https://api.slack.com/apps/)->进入APP->incoming-webhooks->Add New Webhook to Workspace',
+ required: true,
+ })
+ webhook = '';
+
+ async send(body: NotificationBody) {
+ if (!this.webhook) {
+ throw new Error('token不能为空');
+ }
+
+ await this.http.request({
+ url: this.webhook,
+ method: 'POST',
+ data: {
+ text: `${body.title}\n${body.content}\n[查看详情](${body.url})`,
+ },
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/telegram/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/telegram/index.ts
new file mode 100644
index 00000000..0f90f1b3
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/telegram/index.ts
@@ -0,0 +1,51 @@
+import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
+
+@IsNotification({
+ name: 'telegram',
+ title: 'Telegram通知',
+ desc: 'Telegram Bot推送通知',
+})
+export class TelegramNotification extends BaseNotification {
+ @NotificationInput({
+ title: 'Bot Token',
+ component: {
+ placeholder: '123456789:ABCdefGhijklmnopqrstUVWXyz',
+ },
+ helper: '[token获取](https://core.telegram.org/bots/features#botfather)',
+ required: true,
+ })
+ botToken = '';
+
+ @NotificationInput({
+ title: '聊天ID',
+ component: {
+ placeholder: '聊天ID,例如 -123456789 或 @channelusername',
+ },
+ helper: '聊天的唯一标识符或用户名',
+ required: true,
+ })
+ chatId = '';
+
+ async send(body: NotificationBody) {
+ if (!this.botToken || !this.chatId) {
+ throw new Error('Bot Token 和聊天ID不能为空');
+ }
+
+ // 构建消息内容
+ const messageContent = `*${body.title}*\n\n${body.content}\n[查看详情](${body.url})`;
+
+ // Telegram API URL
+ const url = `https://api.telegram.org/bot${this.botToken}/sendMessage`;
+
+ // 发送 HTTP 请求
+ await this.http.request({
+ url: url,
+ method: 'POST',
+ data: {
+ chat_id: this.chatId,
+ text: messageContent,
+ parse_mode: 'MarkdownV2', // 或使用 'HTML' 取决于需要的格式
+ },
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/vocechat/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/vocechat/index.ts
new file mode 100644
index 00000000..c7c4f679
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/vocechat/index.ts
@@ -0,0 +1,73 @@
+import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
+
+@IsNotification({
+ name: 'vocechat',
+ title: 'VoceChat通知',
+ desc: 'https://voce.chat',
+})
+export class VoceChatNotification extends BaseNotification {
+ @NotificationInput({
+ title: '服务地址',
+ component: {
+ placeholder: 'https://replace.your.domain',
+ },
+ required: true,
+ })
+ endpoint = '';
+
+ @NotificationInput({
+ title: 'apiKey',
+ component: {
+ placeholder: '',
+ },
+ helper: '[获取APIKEY](https://doc.voce.chat/bot/bot-and-webhook)',
+ required: true,
+ })
+ apiKey = '';
+
+ @NotificationInput({
+ title: '目标类型',
+ component: {
+ name: 'a-select',
+ options: [
+ { value: 'user', label: '用户' },
+ { value: 'channel', label: '频道' },
+ ],
+ },
+ required: true,
+ helper: '发送消息的目标类型',
+ })
+ targetType = '';
+
+ @NotificationInput({
+ title: '目标ID',
+ component: {
+ placeholder: '发送消息的目标ID',
+ },
+ required: true,
+ helper: '目标ID可以是用户ID或频道ID',
+ })
+ targetId = '';
+
+ async send(body: NotificationBody) {
+ if (!this.apiKey) {
+ throw new Error('API Key不能为空');
+ }
+
+ if (!this.targetId) {
+ throw new Error('目标ID不能为空');
+ }
+
+ const url = this.targetType === 'user' ? '/api/bot/send_to_user/' : '/api/bot/send_to_group/';
+ await this.http.request({
+ url: url + this.targetId, // 这是示例API URL,请根据实际API文档调整
+ baseURL: this.endpoint,
+ method: 'POST',
+ headers: {
+ 'x-api-key': this.apiKey,
+ 'Content-Type': 'text/markdown',
+ },
+ data: `# ${body.title}\n\n${body.content}\n[查看详情](${body.url})`,
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/webhook/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/webhook/index.ts
index e425cbef..b813fb8e 100644
--- a/packages/ui/certd-server/src/plugins/plugin-notification/webhook/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-notification/webhook/index.ts
@@ -44,11 +44,22 @@ export class WebhookNotification extends BaseNotification {
})
contentType = '';
+ @NotificationInput({
+ title: 'Headers',
+ component: {
+ name: 'a-textarea',
+ rows: 3,
+ },
+ helper: '一行一个,格式为key:value',
+ required: true,
+ })
+ headers = '';
+
@NotificationInput({
title: '消息body模版',
value: `{
- title:"{title}",
- content:"{content}"
+ "text":"{title}",
+ "desp":"{content}\\n[查看详情]({url})"
}`,
component: {
name: 'a-textarea',
@@ -57,7 +68,7 @@ export class WebhookNotification extends BaseNotification {
col: {
span: 24,
},
- helper: `根据实际的webhook接口,构建一个json对象作为参数,支持{title}和{content}两个变量,变量用{}包裹,字符串需要双引号`,
+ helper: `根据实际的webhook接口,构建一个json对象作为参数,支持变量:{title}、{content}、{url},变量用{}包裹,字符串需要双引号`,
required: true,
})
template = '';
@@ -67,15 +78,21 @@ export class WebhookNotification extends BaseNotification {
throw new Error('模版不能为空');
}
- const bodyStr = this.template.replaceAll('{title}', body.title).replaceAll('{content}', body.content);
+ const bodyStr = this.template.replaceAll('{title}', body.title).replaceAll('{content}', body.content).replaceAll('{url}', body.url);
const data = JSON.parse(bodyStr);
+ const headers: any = {};
+ this.headers.split('\n').forEach(item => {
+ const [key, value] = item.trim().split(':');
+ headers[key] = value;
+ });
await this.http.request({
url: this.webhook,
method: this.method,
headers: {
'Content-Type': `${this.contentType}; charset=UTF-8`,
+ ...headers,
},
data: data,
});