mirror of
https://github.com/certd/certd.git
synced 2026-07-07 04:47:34 +08:00
perf: 【破坏性更新】 证书压缩包不再生成文件存储,而是实时打包下载,证书申请插件不再输出certZip
自定义插件需要压缩包时可以调用new CertReader(certInfo).buildZip() 方式获取
This commit is contained in:
@@ -131,7 +131,6 @@ export class MainConfiguration {
|
||||
setLogger((text: string) => {
|
||||
logger.info(text);
|
||||
});
|
||||
|
||||
logger.info("当前环境:", this.app.getEnv()); // prod
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { CertInfoService } from "../../../modules/monitor/index.js";
|
||||
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
|
||||
import { SelectQueryBuilder } from "typeorm";
|
||||
import { logger } from "@certd/basic";
|
||||
import fs from "fs";
|
||||
import dayjs from "dayjs";
|
||||
import { ApiTags } from "@midwayjs/swagger";
|
||||
import { CertReader } from "@certd/plugin-lib";
|
||||
@@ -167,29 +166,29 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
||||
@Get("/download", { description: Constants.per.authOnly, summary: "下载证书文件" })
|
||||
async download(@Query("id") id: number) {
|
||||
const { userId, projectId } = await this.checkOwner(this.getService(), id, "read");
|
||||
const certInfo = await this.getService().info(id);
|
||||
if (certInfo == null) {
|
||||
const certInfoEntity = await this.getService().info(id);
|
||||
if (certInfoEntity == null) {
|
||||
throw new CommonException("file not found");
|
||||
}
|
||||
if (certInfo.userId !== userId) {
|
||||
if (certInfoEntity.userId !== userId) {
|
||||
throw new CommonException("file not found");
|
||||
}
|
||||
if (projectId && certInfo.projectId !== projectId) {
|
||||
if (projectId && certInfoEntity.projectId !== projectId) {
|
||||
throw new CommonException("file not found");
|
||||
}
|
||||
// koa send file
|
||||
// 下载文件的名称
|
||||
// const filename = file.filename;
|
||||
// 要下载的文件的完整路径
|
||||
const path = certInfo.certFile;
|
||||
if (!path) {
|
||||
throw new CommonException("file not found");
|
||||
if (!certInfoEntity.certInfo) {
|
||||
throw new CommonException("证书数据未生成");
|
||||
}
|
||||
logger.info(`download:${path}`);
|
||||
// 以流的形式下载文件
|
||||
this.ctx.attachment(path);
|
||||
this.ctx.set("Content-Type", "application/octet-stream");
|
||||
|
||||
return fs.createReadStream(path);
|
||||
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
||||
const certReader = new CertReader(certInfo);
|
||||
const zipBuffer = await certReader.buildZip();
|
||||
const filename = certReader.buildZipFilename("cert");
|
||||
|
||||
logger.info(`download cert zip: ${filename}, size: ${zipBuffer.length}`);
|
||||
|
||||
this.ctx.attachment(filename);
|
||||
this.ctx.set("Content-Type", "application/zip");
|
||||
this.ctx.body = zipBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
|
||||
import { BaseController, Constants, PermissionException } from "@certd/lib-server";
|
||||
import { StorageService } from "../../../modules/pipeline/service/storage-service.js";
|
||||
@@ -6,6 +6,7 @@ import { CertReader } from "@certd/plugin-cert";
|
||||
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
|
||||
import { UserGrantSetting } from "../../../modules/mine/service/models.js";
|
||||
import { ApiTags } from "@midwayjs/swagger";
|
||||
import { logger } from "@certd/basic";
|
||||
|
||||
@Provide()
|
||||
@Controller("/api/pi/cert")
|
||||
@@ -57,4 +58,38 @@ export class CertController extends BaseController {
|
||||
const certDetail = CertReader.readCertDetail(crt);
|
||||
return this.ok(certDetail);
|
||||
}
|
||||
|
||||
@Get("/downloadZip", { description: Constants.per.authOnly, summary: "下载流水线证书压缩包" })
|
||||
async downloadZip(@Query("id") id: number) {
|
||||
const { userId } = await this.getProjectUserIdRead();
|
||||
|
||||
const pipelineUserId = await this.pipelineService.getPipelineUserId(id);
|
||||
|
||||
if (pipelineUserId !== userId) {
|
||||
const isAdmin = await this.isAdmin();
|
||||
if (!isAdmin) {
|
||||
throw new PermissionException();
|
||||
}
|
||||
const setting = await this.userSettingsService.getSetting<UserGrantSetting>(pipelineUserId, null, UserGrantSetting, false);
|
||||
if (setting?.allowAdminViewCerts !== true) {
|
||||
throw new PermissionException("该流水线的用户还未授权管理员下载证书,请先让用户在”设置->授权委托“中打开开关");
|
||||
}
|
||||
}
|
||||
|
||||
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
||||
const certInfo = privateVars.cert;
|
||||
if (!certInfo?.crt) {
|
||||
throw new Error("该流水线还未生成证书,请先运行一次流水线");
|
||||
}
|
||||
|
||||
const certReader = new CertReader(certInfo);
|
||||
const zipBuffer = await certReader.buildZip();
|
||||
const filename = certReader.buildZipFilename("cert");
|
||||
|
||||
logger.info(`download pipeline cert zip: ${filename}, size: ${zipBuffer.length}`);
|
||||
|
||||
this.ctx.attachment(filename);
|
||||
this.ctx.set("Content-Type", "application/zip");
|
||||
this.ctx.body = zipBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-58
@@ -1,8 +1,7 @@
|
||||
import { AbstractTaskPlugin, FileItem, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||
import { AbstractTaskPlugin, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||
import { CertConverter, CertReader, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-lib";
|
||||
import dayjs from "dayjs";
|
||||
import type { CertInfo } from "./acme.js";
|
||||
import { CertReader, CertConverter, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-lib";
|
||||
import JSZip from "jszip";
|
||||
|
||||
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
@@ -72,12 +71,6 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
cert?: CertInfo;
|
||||
|
||||
@TaskOutput({
|
||||
title: "域名证书压缩文件",
|
||||
type: "certZip",
|
||||
})
|
||||
certZip?: FileItem;
|
||||
|
||||
async onInstance() {
|
||||
this.userContext = this.ctx.userContext;
|
||||
this.lastStatus = this.ctx.lastStatus as Step;
|
||||
@@ -108,7 +101,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
}
|
||||
this._result.pipelinePrivateVars.cert = cert;
|
||||
|
||||
if (isNew) {
|
||||
if (isNew || !cert.pfx) {
|
||||
try {
|
||||
const converter = new CertConverter({ logger: this.logger });
|
||||
const res = await converter.convert({
|
||||
@@ -137,54 +130,6 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
this.logger.error("转换证书格式失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
const zipFileName = certReader.buildCertFileName("zip", certReader.detail.notBefore);
|
||||
await this.zipCert(cert, zipFileName);
|
||||
} else {
|
||||
this.extendsFiles();
|
||||
}
|
||||
this.certZip = this._result.files[0];
|
||||
}
|
||||
|
||||
async zipCert(cert: CertInfo, filename: string) {
|
||||
const zip = new JSZip();
|
||||
zip.file("证书.pem", cert.crt);
|
||||
zip.file("私钥.pem", cert.key);
|
||||
zip.file("中间证书.pem", cert.ic);
|
||||
zip.file("cert.crt", cert.crt);
|
||||
zip.file("cert.key", cert.key);
|
||||
zip.file("intermediate.crt", cert.ic);
|
||||
zip.file("origin.crt", cert.oc);
|
||||
zip.file("one.pem", cert.one);
|
||||
zip.file("cert.p7b", cert.p7b);
|
||||
if (cert.pfx) {
|
||||
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
|
||||
}
|
||||
if (cert.der) {
|
||||
zip.file("cert.der", Buffer.from(cert.der, "base64"));
|
||||
}
|
||||
if (cert.jks) {
|
||||
zip.file("cert.jks", Buffer.from(cert.jks, "base64"));
|
||||
}
|
||||
|
||||
zip.file(
|
||||
"说明.txt",
|
||||
`证书文件说明
|
||||
cert.crt:证书文件,包含证书链,pem格式
|
||||
cert.key:私钥文件,pem格式
|
||||
intermediate.crt:中间证书文件,pem格式
|
||||
origin.crt:原始证书文件,不含证书链,pem格式
|
||||
one.pem: 证书和私钥简单合并成一个文件,pem格式,crt正文+key正文
|
||||
cert.pfx:pfx格式证书文件,iis服务器使用
|
||||
cert.der:der格式证书文件
|
||||
cert.jks:jks格式证书文件,java服务器使用
|
||||
`
|
||||
);
|
||||
|
||||
const content = await zip.generateAsync({ type: "nodebuffer" });
|
||||
this.saveFile(filename, content);
|
||||
this.logger.info(`已保存文件:${filename}`);
|
||||
}
|
||||
|
||||
formatCert(pem: string) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractTaskPlugin, FileItem, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { CertInfo, CertReader } from "@certd/plugin-cert";
|
||||
import dayjs from "dayjs";
|
||||
import { get } from "lodash-es";
|
||||
@@ -28,17 +28,6 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
@TaskInput({
|
||||
title: "证书压缩文件",
|
||||
helper: "请选择前置任务输出的域名证书压缩文件",
|
||||
component: {
|
||||
name: "output-selector",
|
||||
from: [":certZip:"],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
certZip!: FileItem;
|
||||
|
||||
@TaskInput({
|
||||
title: "接收邮箱",
|
||||
component: {
|
||||
@@ -146,18 +135,18 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
|
||||
`;
|
||||
data.content = content;
|
||||
data.title = title;
|
||||
const file = this.certZip;
|
||||
if (!file) {
|
||||
throw new Error("证书压缩文件还未生成,重新运行证书任务");
|
||||
}
|
||||
|
||||
const zipBuffer = await certReader.buildZip();
|
||||
const zipFilename = certReader.buildZipFilename("cert");
|
||||
|
||||
await this.ctx.emailService.sendByTemplate({
|
||||
type: "sendCert",
|
||||
data,
|
||||
receivers: this.email,
|
||||
attachments: [
|
||||
{
|
||||
filename: file.filename,
|
||||
path: file.path,
|
||||
filename: zipFilename,
|
||||
content: zipBuffer,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user