Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev

This commit is contained in:
xiaojunnuo
2025-12-17 10:02:30 +08:00
10 changed files with 130 additions and 84 deletions

View File

@@ -9,7 +9,8 @@ export type EmailSend = {
export type EmailSendByTemplateReq = {
type: string;
data: any;
email: { receivers: string[]; attachments?: any[] };
receivers: string[];
attachments?: any[];
};
export interface IEmailService {

View File

@@ -145,6 +145,9 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
},
},
},
search: {
col: { span: 3 },
},
form: {
afterSubmit({ form, res, mode }) {
if (mode === "add") {
@@ -262,6 +265,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
type: "number",
search: {
show: true,
col: { span: 3 },
},
column: {
width: 100,
@@ -277,6 +281,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
show: computed(() => {
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
}),
col: { span: 3 },
},
form: {
show: false,
@@ -297,6 +302,7 @@ export default function ({ crudExpose, context: { selectedRowKeys } }: CreateCru
component: {
name: "a-input",
},
col: { span: 3 },
},
form: {
rules: [{ required: true, message: t("certd.fields.required") }],

View File

@@ -33,7 +33,20 @@ export class PipelineController extends CrudController<PipelineService> {
async page(@Body(ALL) body) {
const isAdmin = await this.authService.isAdmin(this.ctx);
const publicSettings = await this.sysSettingsService.getPublicSettings();
if (!(publicSettings.managerOtherUserPipeline && isAdmin)) {
let onlyOther = false
if(isAdmin){
if(publicSettings.managerOtherUserPipeline){
//管理员管理 其他用户
if( body.query.userId === -1){
//如果只查询其他用户
onlyOther = true;
delete body.query.userId;
}
}else{
body.query.userId = this.getUserId();
}
}else{
body.query.userId = this.getUserId();
}
@@ -44,6 +57,9 @@ export class PipelineController extends CrudController<PipelineService> {
if (title) {
qb.andWhere('(title like :title or content like :content)', { title: `%${title}%`, content: `%${title}%` });
}
if(onlyOther){
qb.andWhere('user_id != :userId', { userId: this.getUserId() });
}
};
if (!body.sort || !body.sort?.prop) {
body.sort = { prop: 'order', asc: false };
@@ -142,26 +158,36 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/batchDelete', { summary: Constants.per.authOnly })
async batchDelete(@Body('ids') ids: number[]) {
await this.service.batchDelete(ids, this.getUserId());
const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchDelete(ids, userId);
return this.ok({});
}
@Post('/batchUpdateGroup', { summary: Constants.per.authOnly })
async batchUpdateGroup(@Body('ids') ids: number[], @Body('groupId') groupId: number) {
await this.service.batchUpdateGroup(ids, groupId, this.getUserId());
const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateGroup(ids, groupId, userId);
return this.ok({});
}
@Post('/batchUpdateTrigger', { summary: Constants.per.authOnly })
async batchUpdateTrigger(@Body('ids') ids: number[], @Body('trigger') trigger: any) {
await this.service.batchUpdateTrigger(ids, trigger, this.getUserId());
const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateTrigger(ids, trigger, userId);
return this.ok({});
}
@Post('/batchUpdateNotification', { summary: Constants.per.authOnly })
async batchUpdateNotification(@Body('ids') ids: number[], @Body('notification') notification: any) {
await this.service.batchUpdateNotifications(ids, notification, this.getUserId());
const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? undefined : this.getUserId() ;
await this.service.batchUpdateNotifications(ids, notification, userId);
return this.ok({});
}

View File

@@ -108,9 +108,7 @@ export class CodeService {
await this.emailService.sendByTemplate({
type: templateData.notificationType,
data: templateData,
email:{
receivers: [email],
},
receivers: [email],
});
const key = this.buildEmailCodeKey(email,opts?.verificationType);

View File

@@ -123,9 +123,7 @@ export class EmailService implements IEmailService {
content: '测试邮件,from certd',
url:"https://certd.handfree.work",
},
email: {
receivers: [receiver],
},
receivers: [receiver],
});
}
@@ -186,8 +184,9 @@ export class EmailService implements IEmailService {
content = await addon.buildDefaultContent({ data: req.data })
}
return await this.send({
...req.email,
...content
...content,
receivers: req.receivers,
attachments: req.attachments,
})
}
}

View File

@@ -777,17 +777,29 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
async batchDelete(ids: number[], userId: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
for (const id of ids) {
await this.checkUserId(id, userId);
if (userId) {
await this.checkUserId(id, userId);
}
await this.delete(id);
}
}
async batchUpdateGroup(ids: number[], groupId: number, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
await this.repository.update(
{
id: In(ids),
userId
...query
},
{ groupId }
);
@@ -795,11 +807,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
const list = await this.find({
where: {
id: In(ids),
userId
...query
}
});
@@ -822,11 +840,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
}
const query :any = {}
if(userId && userId>0){
query.userId = userId;
}
const list = await this.find({
where: {
id: In(ids),
userId
...query
}
});

View File

@@ -34,7 +34,7 @@ export class EmailNotification extends BaseNotification {
await this.ctx.emailService.sendByTemplate({
type: body.notificationType,
data: templateData,
email: emailSend
...emailSend
})
// await this.ctx.emailService.send({

View File

@@ -1,5 +1,5 @@
import {AbstractTaskPlugin, FileItem, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
import {CertInfo, CertReader} from "@certd/plugin-cert";
import { AbstractTaskPlugin, FileItem, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo, CertReader } from "@certd/plugin-cert";
import dayjs from "dayjs";
import { get } from 'lodash-es';
@@ -9,7 +9,7 @@ import { get } from 'lodash-es';
icon: 'ion:mail-outline',
desc: '通过邮件发送证书',
group: pluginGroups.other.key,
showRunStrategy:false,
showRunStrategy: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
@@ -45,7 +45,7 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
component: {
name: 'EmailSelector',
vModel: 'value',
mode:"tags",
mode: "tags",
},
required: true,
})
@@ -78,40 +78,40 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
*/
@TaskInput({
title: '邮件标题',
component: {
name: 'a-input',
vModel: 'value',
placeholder:`证书申请成功【$\{mainDomain}】`,
},
helper: '请输入邮件标题否则将使用默认标题\n模板变量主域名=$\{mainDomain}、全部域名=$\{domains}、过期时间=$\{expiresTime}、备注=$\{remark}、证书PEM=$\{crt}、证书私钥=$\{key}、中间证书/CA证书=$\{ic}',
required: false,
})
title!: string;
// @TaskInput({
// title: '邮件标题',
// component: {
// name: 'a-input',
// vModel: 'value',
// placeholder: `证书申请成功【$\{mainDomain}】`,
// },
// helper: '请输入邮件标题否则将使用默认标题\n模板变量主域名=$\{mainDomain}、全部域名=$\{domains}、过期时间=$\{expiresTime}、备注=$\{remark}、证书PEM=$\{crt}、证书私钥=$\{key}、中间证书/CA证书=$\{ic}',
// required: false,
// })
// title!: string;
@TaskInput({
title: '邮件模版',
component: {
name: 'a-textarea',
vModel: 'value',
autosize: {
minRows: 6,
maxRows: 10,
},
placeholder: `
<div>
<p>证书申请成功</p>
<p>域名:$\{domains}</p>
<p>证书有效期:$\{expiresTime}</p>
<p>备注:$\{remark}</p>
</div>
`,
},
helper: `请输入模版内容否则将使用默认模版,模板变量同上`,
required: false,
})
template!: string;
// @TaskInput({
// title: '邮件模版',
// component: {
// name: 'a-textarea',
// vModel: 'value',
// autosize: {
// minRows: 6,
// maxRows: 10,
// },
// placeholder: `
// <div>
// <p>证书申请成功</p>
// <p>域名:$\{domains}</p>
// <p>证书有效期:$\{expiresTime}</p>
// <p>备注:$\{remark}</p>
// </div>
// `,
// },
// helper: `请输入模版内容否则将使用默认模版,模板变量同上`,
// required: false,
// })
// template!: string;
@TaskInput({
title: '备注',
@@ -123,7 +123,7 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
})
remark!: string;
async onInstance() {}
async onInstance() { }
async execute(): Promise<void> {
this.logger.info(`开始发送邮件`);
@@ -132,41 +132,32 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
const domains = certReader.getAllDomains().join(',');
const data = {
const data: any = {
mainDomain,
domains,
expiresTime: dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss"),
remark:this.remark ||"",
remark: this.remark || "",
crt: this.cert.crt,
key: this.cert.key,
ic: this.cert.ic
ic: this.cert.ic,
url: ""
}
let title = `证书申请成功【${mainDomain}`;
let html = `
<div>
<p>证书申请成功</p>
<p>域名:${domains}</p>
<p>证书有效期:${data.expiresTime}</p>
<p>备注:${this.remark||""}</p>
</div>
let content = `证书申请成功
域名:${domains}
证书有效期:${data.expiresTime}
备注:${this.remark || ""}
`;
if (this.title) {
const compile = this.compile(this.title);
title = compile(data);
}
if (this.template) {
const compile = this.compile(this.template);
html = compile(data);
}
data.content = content;
data.title = title
const file = this.certZip
if (!file) {
throw new Error('证书压缩文件还未生成,重新运行证书任务');
}
await this.ctx.emailService.send({
subject:title,
html: html,
await this.ctx.emailService.sendByTemplate({
type: "common",
data,
receivers: this.email,
attachments: [
{
@@ -178,7 +169,7 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
}
compile(templateString: string) {
return function(data) {
return function (data) {
return templateString.replace(/\${(.*?)}/g, (match, key) => {
const value = get(data, key, '');
return String(value);

View File

@@ -29,7 +29,7 @@ export class CommonEmailTemplateProvider extends BaseEmailTemplateProvider imple
async buildDefaultContent(req:BuildContentReq) {
const defaultTemplate = new CommonEmailTemplateProvider()
defaultTemplate.titleTemplate = "${title}"
defaultTemplate.contentTemplate = "${content} \n\n 查看详情:${url}"
defaultTemplate.contentTemplate = "${content} \n\n ${url?'查看详情:'+url:''}"
defaultTemplate.formatType = "text"
return await defaultTemplate.buildContent(req)
}

View File

@@ -11,7 +11,7 @@ import {CertApplyPluginNames, CertInfo, CertReader} from "@certd/plugin-cert";
name: 'DeployCertToTencentEO',
title: '腾讯云-部署到腾讯云EO',
icon: 'svg:icon-tencentcloud',
desc: '腾讯云边缘安全加速平台EdgeOne(EO),必须配置上传证书到腾讯云任务',
desc: '腾讯云边缘安全加速平台EdgeOne(EO)',
group: pluginGroups.tencent.key,
default: {
strategy: {
@@ -126,6 +126,7 @@ export class DeployCertToTencentEO extends AbstractTaskPlugin {
},
],
};
this.logger.info('设置腾讯云EO证书参数:', JSON.stringify(params));
await this.doRequest(client, params);
}