chore: 支持手动上传证书并部署

This commit is contained in:
xiaojunnuo
2025-03-18 00:52:50 +08:00
parent 29a6a992f0
commit de40be430b
74 changed files with 1040 additions and 597 deletions
@@ -1,10 +1,10 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService, CodeException, CommonException, Constants, PageReq } from '@certd/lib-server';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { CertInfoEntity } from '../entity/cert-info.js';
import { utils } from '@certd/basic';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { BaseService, CodeException, Constants, PageReq } from "@certd/lib-server";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { Repository } from "typeorm";
import { CertInfoEntity } from "../entity/cert-info.js";
import { utils } from "@certd/basic";
import { CertInfo, CertReader } from "@certd/plugin-cert";
export type UploadCertReq = {
id?: number;
@@ -13,6 +13,7 @@ export type UploadCertReq = {
userId?: number;
};
@Provide("CertInfoService")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CertInfoService extends BaseService<CertInfoEntity> {
@@ -168,17 +169,4 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
return bean;
}
async upload(body: { id?: number; userId?:number ;cert: CertInfo }) {
const { id, userId, cert } = body;
if (!cert) {
throw new CommonException("cert can't be empty");
}
const res = await this.updateCert({
id,
certReader: new CertReader(cert),
fromType: 'upload',
userId
});
return res.id;
}
}
@@ -0,0 +1,178 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { BaseService, CommonException } from "@certd/lib-server";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { EntityManager, Repository } from "typeorm";
import { CertInfoEntity } from "../entity/cert-info.js";
import { logger } from "@certd/basic";
import { CertInfo, CertReader } from "@certd/plugin-cert";
import { PipelineService } from "../../pipeline/service/pipeline-service.js";
import { CertInfoService } from "./cert-info-service.js";
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
import { nanoid } from "nanoid";
export type UploadCertReq = {
id?: number;
certReader: CertReader;
fromType?: string;
userId?: number;
};
export type UpdateCertReq = {
id: number;
cert: CertInfo;
userId?: number;
};
export type CreateUploadPipelineReq = {
cert: CertInfo;
userId: number;
};
@Provide("CertUploadService")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CertUploadService extends BaseService<CertInfoEntity> {
@InjectEntityModel(CertInfoEntity)
repository: Repository<CertInfoEntity>;
@Inject()
pipelineService: PipelineService;
@Inject()
certInfoService: CertInfoService;
//@ts-ignore
getRepository() {
return this.repository;
}
/**
* 更新证书,触发流水线
* @param req
*/
async updateCert(req: UpdateCertReq) {
const certInfoEntity = await this.certInfoService.info(req.id);
if (!certInfoEntity) {
throw new CommonException("cert not found");
}
if(certInfoEntity.fromType !== 'upload') {
throw new CommonException("cert can't be custom upload");
}
await this.uploadCert(this.repository.manager,{
id: req.id,
fromType: 'upload',
userId: req.userId,
certReader: new CertReader(req.cert)
})
if (certInfoEntity.pipelineId) {
logger.info( `触发流水线部署:${certInfoEntity.pipelineId}`)
await this.pipelineService.trigger(certInfoEntity.pipelineId)
}
}
async createUploadCertPipeline(body:CreateUploadPipelineReq) {
const { userId, cert } = body;
if (!cert) {
throw new CommonException("cert can't be empty");
}
const certReader = new CertReader(cert)
return await this.transaction(async (tx:EntityManager)=>{
const newCertInfo = await this.uploadCert(tx,{
certReader: certReader,
fromType: 'upload',
userId
});
const pipelineTitle = certReader.getAllDomains()[0] +"上传证书自动部署";
const notifications = [];
notifications.push({
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: 0,
title: "默认通知",
});
let pipeline = {
id: nanoid(10),
title: pipelineTitle,
runnableType: "pipeline",
stages: [
{
id: nanoid(10),
title: "上传证书解析阶段",
maxTaskCount: 1,
runnableType: "stage",
tasks: [
{
id: nanoid(10),
title: "上传证书解析任务",
runnableType: "task",
steps: [
{
id: nanoid(10),
title: "上传证书解析",
runnableType: "step",
input: {
certInfoId: newCertInfo.id,
domains: newCertInfo.domains.split(','),
},
strategy: {
runStrategy: 0, // 正常执行
},
type: "CertApplyUpload",
},
],
},
],
},
],
triggers:[],
notifications,
}
const newPipeline = await tx.getRepository(PipelineEntity).save({
userId,
title: pipelineTitle,
type:"cert",
from:"cert_upload",
content: JSON.stringify(pipeline),
keepHistory:20,
})
newCertInfo.pipelineId = newPipeline.id;
await tx.getRepository(CertInfoEntity).save({
id: newCertInfo.id,
pipelineId: newPipeline.id
});
return newCertInfo.id
})
}
private async uploadCert(tx:EntityManager,req: UploadCertReq) {
const bean = new CertInfoEntity();
const { id, fromType,userId, certReader } = req;
if (id) {
bean.id = id;
} else {
bean.fromType = fromType;
bean.userId = userId
}
const certInfo = certReader.toCertInfo();
bean.certInfo = JSON.stringify(certInfo);
bean.applyTime = new Date().getTime();
const domains = certReader.detail.domains.altNames;
bean.domains = domains.join(',');
bean.domain = domains[0];
bean.domainCount = domains.length;
bean.expiresTime = certReader.expires;
bean.certProvider = certReader.detail.issuer.commonName;
await tx.getRepository(CertInfoEntity).save(bean);
return bean;
}
}