mirror of https://github.com/certd/certd
perf: 手动上传证书部署流水线
parent
fedf90ea78
commit
fbb66f3c43
|
@ -4,6 +4,7 @@ import type { CertInfo } from "./acme.js";
|
|||
import { CertReader } from "./cert-reader.js";
|
||||
import JSZip from "jszip";
|
||||
import { CertConverter } from "./convert.js";
|
||||
export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
|
||||
|
||||
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
|
@ -76,6 +77,16 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
|||
|
||||
abstract onInit(): Promise<void>;
|
||||
|
||||
//必须output之后执行
|
||||
async emitCertApplySuccess() {
|
||||
const emitter = this.ctx.emitter;
|
||||
const value = {
|
||||
cert: this.cert,
|
||||
file: this._result.files[0].path,
|
||||
};
|
||||
await emitter.emit(EVENT_CERT_APPLY_SUCCESS, value);
|
||||
}
|
||||
|
||||
async output(certReader: CertReader, isNew: boolean) {
|
||||
const cert: CertInfo = certReader.toCertInfo();
|
||||
this.cert = cert;
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
import { NotificationBody, Step, TaskEmitter, TaskInput } from "@certd/pipeline";
|
||||
import { NotificationBody, Step, TaskInput } from "@certd/pipeline";
|
||||
import dayjs from "dayjs";
|
||||
import { CertReader } from "./cert-reader.js";
|
||||
import { pick } from "lodash-es";
|
||||
import { CertApplyBaseConvertPlugin } from "./base-convert.js";
|
||||
|
||||
export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
|
||||
|
||||
export async function emitCertApplySuccess(emitter: TaskEmitter, cert: CertReader) {
|
||||
await emitter.emit(EVENT_CERT_APPLY_SUCCESS, cert);
|
||||
}
|
||||
|
||||
export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
||||
@TaskInput({
|
||||
title: "邮箱",
|
||||
|
@ -75,7 +69,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
|||
if (cert != null) {
|
||||
await this.output(cert, true);
|
||||
|
||||
await emitCertApplySuccess(this.ctx.emitter, cert);
|
||||
await this.emitCertApplySuccess();
|
||||
//清空后续任务的状态,让后续任务能够重新执行
|
||||
this.clearLastStatus();
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import { CertInfo } from "../acme";
|
||||
export interface ICertApplyUploadService {
|
||||
getCertInfo: (opts: { certId: number; userId: number }) => Promise<any>;
|
||||
updateCert: (opts: { certId: number; cert: CertInfo; userId: number }) => Promise<any>;
|
||||
}
|
|
@ -2,9 +2,8 @@ import { IsTaskPlugin, pluginGroups, RunStrategy, Step, TaskInput, TaskOutput }
|
|||
import type { CertInfo } from "../acme.js";
|
||||
import { CertReader } from "../cert-reader.js";
|
||||
import { CertApplyBaseConvertPlugin } from "../base-convert.js";
|
||||
export * from "./d.js";
|
||||
import dayjs from "dayjs";
|
||||
import { ICertApplyUploadService } from "./d";
|
||||
|
||||
export { CertReader };
|
||||
export type { CertInfo };
|
||||
@IsTaskPlugin({
|
||||
|
@ -84,7 +83,7 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
|
|||
}
|
||||
`,
|
||||
})
|
||||
certInfoId!: string;
|
||||
uploadCert!: CertInfo;
|
||||
|
||||
@TaskOutput({
|
||||
title: "证书MD5",
|
||||
|
@ -100,14 +99,7 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
|
|||
async onInit(): Promise<void> {}
|
||||
|
||||
async getCertFromStore() {
|
||||
const certApplyUploadService: ICertApplyUploadService = await this.ctx.serviceGetter.get("CertApplyUploadService");
|
||||
|
||||
const certInfo = await certApplyUploadService.getCertInfo({
|
||||
certId: Number(this.certInfoId),
|
||||
userId: this.pipeline.userId,
|
||||
});
|
||||
|
||||
const certReader = new CertReader(certInfo);
|
||||
const certReader = new CertReader(this.uploadCert);
|
||||
if (!certReader.expires && certReader.expires < new Date().getTime()) {
|
||||
throw new Error("证书已过期,停止部署,请重新上传证书");
|
||||
}
|
||||
|
@ -121,39 +113,43 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
|
|||
|
||||
const leftDays = dayjs(certReader.expires).diff(dayjs(), "day");
|
||||
this.logger.info(`证书过期时间${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${leftDays}天`);
|
||||
const lastCrtMd5 = this.lastStatus?.status?.output?.certMd5;
|
||||
this.logger.info("证书MD5", crtMd5);
|
||||
this.logger.info("上次证书MD5", lastCrtMd5);
|
||||
if (lastCrtMd5 === crtMd5) {
|
||||
this.logger.info("证书无变化,跳过");
|
||||
//输出证书MD5
|
||||
this.certMd5 = crtMd5;
|
||||
await this.output(certReader, false);
|
||||
return "skip";
|
||||
|
||||
if (!this.ctx.inputChanged) {
|
||||
this.logger.info("输入参数无变化");
|
||||
const lastCrtMd5 = this.lastStatus?.status?.output?.certMd5;
|
||||
this.logger.info("证书MD5", crtMd5);
|
||||
this.logger.info("上次证书MD5", lastCrtMd5);
|
||||
if (lastCrtMd5 === crtMd5) {
|
||||
this.logger.info("证书无变化,跳过");
|
||||
//输出证书MD5
|
||||
this.certMd5 = crtMd5;
|
||||
await this.output(certReader, false);
|
||||
return "skip";
|
||||
}
|
||||
this.logger.info("证书有变化,重新部署");
|
||||
} else {
|
||||
this.logger.info("输入参数有变化,重新部署");
|
||||
}
|
||||
this.logger.info("证书有变化,重新部署");
|
||||
|
||||
this.clearLastStatus();
|
||||
//输出证书MD5
|
||||
this.certMd5 = crtMd5;
|
||||
await this.output(certReader, true);
|
||||
|
||||
//必须output之后执行
|
||||
await this.emitCertApplySuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
async onCertUpdate(data: any) {
|
||||
const certApplyUploadService = await this.ctx.serviceGetter.get("CertApplyUploadService");
|
||||
|
||||
const res = await certApplyUploadService.updateCert({
|
||||
certId: this.certInfoId,
|
||||
userId: this.ctx.user.id,
|
||||
cert: {
|
||||
crt: data.crt,
|
||||
key: data.key,
|
||||
},
|
||||
});
|
||||
|
||||
const certReader = new CertReader(data);
|
||||
return {
|
||||
input: {
|
||||
domains: res.domains,
|
||||
uploadCert: {
|
||||
crt: data.crt,
|
||||
key: data.key,
|
||||
},
|
||||
domains: certReader.getAllDomains(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export { EVENT_CERT_APPLY_SUCCESS } from "./cert-plugin/base-convert.js";
|
||||
|
||||
export * from "./cert-plugin/index.js";
|
||||
export * from "./cert-plugin/lego/index.js";
|
||||
export * from "./cert-plugin/custom/index.js";
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
"lucide-vue-next": "^0.477.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^4.0.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"pinia": "2.1.7",
|
||||
|
|
|
@ -5,8 +5,8 @@ import OutputSelector from "/@/components/plugins/common/output-selector/index.v
|
|||
import DnsProviderSelector from "/@/components/plugins/cert/dns-provider-selector/index.vue";
|
||||
import DomainsVerifyPlanEditor from "/@/components/plugins/cert/domains-verify-plan-editor/index.vue";
|
||||
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||
import CertInfoUpdater from "/@/views/certd/monitor/cert/updater/index.vue";
|
||||
import InputPassword from "./common/input-password.vue";
|
||||
import CertInfoUpdater from "/@/views/certd/pipeline/cert-upload/index.vue";
|
||||
import ApiTest from "./common/api-test.vue";
|
||||
export * from "./cert/index.js";
|
||||
export default {
|
||||
|
|
|
@ -109,24 +109,23 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
},
|
||||
copy: { show: false },
|
||||
edit: { show: false },
|
||||
upload: {
|
||||
show: compute(({ row }) => {
|
||||
return row.fromType === "upload";
|
||||
}),
|
||||
order: 4,
|
||||
title: "更新证书",
|
||||
type: "link",
|
||||
icon: "ion:upload",
|
||||
async click({ row }) {
|
||||
await openUpdateCertDialog({
|
||||
id: row.id,
|
||||
});
|
||||
},
|
||||
},
|
||||
remove: {
|
||||
order: 10,
|
||||
show: false,
|
||||
},
|
||||
download: {
|
||||
order: 9,
|
||||
title: "下载证书",
|
||||
type: "link",
|
||||
icon: "ant-design:download-outlined",
|
||||
async click({ row }) {
|
||||
if (!row.certFile) {
|
||||
notification.error({ message: "证书还未生成,请先运行流水线" });
|
||||
return;
|
||||
}
|
||||
window.open("/api/monitor/cert/download?id=" + row.id);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<template #header>
|
||||
<div class="title">
|
||||
证书仓库
|
||||
<span class="sub">从流水线生成的证书,后续将支持手动上传证书并部署</span>
|
||||
<span class="sub">从流水线生成的证书</span>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
|
@ -11,12 +11,12 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { createApi } from "./api";
|
||||
|
||||
defineOptions({
|
||||
name: "CertStore"
|
||||
name: "CertStore",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ export async function GetList(query: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ export async function AddObj(obj: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ export async function UpdateObj(obj: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ export async function DelObj(id: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ export async function GetObj(id: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ export async function GetDetail(id: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ export async function Save(pipelineEntity: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
data: pipelineEntity
|
||||
data: pipelineEntity,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ export async function Trigger(id: any, stepId?: string) {
|
|||
return await request({
|
||||
url: apiPrefix + "/trigger",
|
||||
method: "post",
|
||||
params: { id, stepId }
|
||||
params: { id, stepId },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ export async function Cancel(historyId: any) {
|
|||
return await request({
|
||||
url: apiPrefix + "/cancel",
|
||||
method: "post",
|
||||
params: { historyId }
|
||||
params: { historyId },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ export async function BatchUpdateGroup(pipelineIds: number[], groupId: number):
|
|||
return await request({
|
||||
url: apiPrefix + "/batchUpdateGroup",
|
||||
method: "post",
|
||||
data: { ids: pipelineIds, groupId }
|
||||
data: { ids: pipelineIds, groupId },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ export async function BatchDelete(pipelineIds: number[]): Promise<CertInfo> {
|
|||
return await request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids: pipelineIds }
|
||||
data: { ids: pipelineIds },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -96,14 +96,14 @@ export async function GetFiles(pipelineId: number) {
|
|||
return await request({
|
||||
url: historyApiPrefix + "/files",
|
||||
method: "post",
|
||||
params: { pipelineId }
|
||||
params: { pipelineId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetCount() {
|
||||
return await request({
|
||||
url: apiPrefix + "/count",
|
||||
method: "post"
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,6 @@ export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
|||
return await request({
|
||||
url: certApiPrefix + "/get",
|
||||
method: "post",
|
||||
params: { id: pipelineId }
|
||||
params: { id: pipelineId },
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
<template>
|
||||
<div class="cert-info-updater w-full flex items-center">
|
||||
<div class="flex-o">
|
||||
<fs-values-format :model-value="modelValue" :dict="certInfoDict" />
|
||||
<a-tag>{{ domain }}</a-tag>
|
||||
<fs-button type="primary" size="small" class="ml-1" icon="ion:upload" text="更新证书" @click="onUploadClick" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { inject } from "vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { certInfoApi } from "../api";
|
||||
import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use";
|
||||
import { computed, inject } from "vue";
|
||||
import { useCertUpload } from "./use";
|
||||
import { getAllDomainsFromCrt } from "/@/views/certd/pipeline/utils";
|
||||
|
||||
defineOptions({
|
||||
name: "CertInfoUpdater",
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: number | string;
|
||||
modelValue?: { crt: string; key: string };
|
||||
type?: string;
|
||||
placeholder?: string;
|
||||
size?: string;
|
||||
|
@ -26,33 +25,28 @@ const props = defineProps<{
|
|||
}>();
|
||||
const emit = defineEmits(["updated", "update:modelValue"]);
|
||||
|
||||
const certInfoDict = dict({
|
||||
value: "id",
|
||||
label: "domain",
|
||||
getNodesByValues: async (values: any[]) => {
|
||||
const res = await certInfoApi.GetOptionsByIds(values);
|
||||
if (res.length > 0) {
|
||||
emit("updated", {
|
||||
domains: res[0].domains,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
},
|
||||
const { openUpdateCertDialog } = useCertUpload();
|
||||
|
||||
const domain = computed(() => {
|
||||
if (!props.modelValue?.crt) {
|
||||
return "";
|
||||
}
|
||||
const domains = getAllDomainsFromCrt(props.modelValue?.crt);
|
||||
|
||||
return domains[0];
|
||||
});
|
||||
|
||||
const { openUpdateCertDialog } = useCertUpload();
|
||||
function onUpdated(res: any) {
|
||||
if (!props.modelValue) {
|
||||
emit("update:modelValue", res.id);
|
||||
}
|
||||
emit("updated", res);
|
||||
function onUpdated(res: { uploadCert: any }) {
|
||||
debugger;
|
||||
emit("update:modelValue", res.uploadCert);
|
||||
const domains = getAllDomainsFromCrt(res.uploadCert.crt);
|
||||
emit("updated", { domains });
|
||||
}
|
||||
|
||||
const pipeline: any = inject("pipeline");
|
||||
function onUploadClick() {
|
||||
debugger;
|
||||
openUpdateCertDialog({
|
||||
id: props.modelValue,
|
||||
pipelineId: pipeline.id,
|
||||
onSubmit: onUpdated,
|
||||
});
|
||||
}
|
|
@ -1,23 +1,62 @@
|
|||
import { compute, useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||
import * as api from "./api";
|
||||
import { omit, cloneDeep, set } from "lodash-es";
|
||||
import { cloneDeep, omit } from "lodash-es";
|
||||
import { useReference } from "/@/use/use-refrence";
|
||||
import { ref } from "vue";
|
||||
import * as pluginApi from "../api.plugin";
|
||||
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
|
||||
import { notification } from "ant-design-vue";
|
||||
import * as api from "../api";
|
||||
import { checkPipelineLimit, getAllDomainsFromCrt } from "/@/views/certd/pipeline/utils";
|
||||
import { useRouter } from "vue-router";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export function useCertUpload() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const router = useRouter();
|
||||
|
||||
const certInputs = {
|
||||
"uploadCert.crt": {
|
||||
title: "证书",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN CERTIFICATE-----\n...\n...\n-----END CERTIFICATE-----",
|
||||
},
|
||||
},
|
||||
helper: "选择pem格式证书文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
},
|
||||
},
|
||||
"uploadCert.key": {
|
||||
title: "证书私钥",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN PRIVATE KEY-----\n...\n...\n-----END PRIVATE KEY----- ",
|
||||
},
|
||||
},
|
||||
helper: "选择pem格式证书私钥文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function buildUploadCertPluginInputs(getFormData: any) {
|
||||
const plugin: any = await pluginApi.GetPluginDefine("CertApplyUpload");
|
||||
const inputs: any = {};
|
||||
for (const inputKey in plugin.input) {
|
||||
if (inputKey === "certInfoId" || inputKey === "domains") {
|
||||
if (inputKey === "uploadCert" || inputKey === "domains") {
|
||||
continue;
|
||||
}
|
||||
const inputDefine = cloneDeep(plugin.input[inputKey]);
|
||||
|
@ -66,42 +105,7 @@ export function useCertUpload() {
|
|||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
"cert.crt": {
|
||||
title: "证书",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN CERTIFICATE-----\n...\n...\n-----END CERTIFICATE-----",
|
||||
},
|
||||
},
|
||||
helper: "选择pem格式证书文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
},
|
||||
},
|
||||
"cert.key": {
|
||||
title: "证书私钥",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN PRIVATE KEY-----\n...\n...\n-----END PRIVATE KEY----- ",
|
||||
},
|
||||
},
|
||||
helper: "选择pem格式证书私钥文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
},
|
||||
},
|
||||
...cloneDeep(certInputs),
|
||||
...inputs,
|
||||
notification: {
|
||||
title: "失败通知",
|
||||
|
@ -128,6 +132,9 @@ export function useCertUpload() {
|
|||
saveRemind: false,
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
const cert = form.uploadCert;
|
||||
const domains = getAllDomainsFromCrt(cert.crt);
|
||||
|
||||
const notifications = [];
|
||||
if (form.notification != null) {
|
||||
notifications.push({
|
||||
|
@ -138,18 +145,54 @@ export function useCertUpload() {
|
|||
});
|
||||
}
|
||||
|
||||
const req = {
|
||||
id: form.id,
|
||||
cert: form.cert,
|
||||
pipeline: {
|
||||
input: omit(form, ["id", "cert", "notification", "notificationTarget"]),
|
||||
notifications,
|
||||
},
|
||||
const pipelineTitle = domains[0] + "上传证书部署";
|
||||
const input = omit(form, ["id", "cert", "notification", "notificationTarget"]);
|
||||
const pipeline = {
|
||||
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: {
|
||||
cert: cert,
|
||||
domains: domains,
|
||||
...input,
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0, // 正常执行
|
||||
},
|
||||
type: "CertApplyUpload",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
notifications,
|
||||
};
|
||||
const res = await api.UploadCert(req);
|
||||
|
||||
const id = await api.Save({
|
||||
title: pipeline.title,
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistoryCount: 30,
|
||||
type: "cert_upload",
|
||||
});
|
||||
router.push({
|
||||
path: "/certd/pipeline/detail",
|
||||
query: { id: res.pipelineId, editMode: "true" },
|
||||
query: { id: id, editMode: "true" },
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -161,61 +204,22 @@ export function useCertUpload() {
|
|||
wrapperRef.value = wrapper;
|
||||
}
|
||||
|
||||
async function openUpdateCertDialog(opts: { id?: any; onSubmit?: any; pipelineId?: any }) {
|
||||
async function openUpdateCertDialog(opts: { onSubmit?: any }) {
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
"cert.crt": {
|
||||
title: "证书",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN CERTIFICATE-----\n...\n...\n-----END CERTIFICATE-----",
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
},
|
||||
},
|
||||
"cert.key": {
|
||||
title: "私钥",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN PRIVATE KEY-----\n...\n...\n-----END PRIVATE KEY----- ",
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
},
|
||||
},
|
||||
...cloneDeep(certInputs),
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
title: "更新证书",
|
||||
title: "手动上传证书",
|
||||
saveRemind: false,
|
||||
},
|
||||
async afterSubmit() {
|
||||
notification.success({ message: "更新成功" });
|
||||
},
|
||||
async afterSubmit() {},
|
||||
async doSubmit({ form }: any) {
|
||||
const req = {
|
||||
id: opts.id,
|
||||
pipelineId: opts.pipelineId,
|
||||
cert: form.cert,
|
||||
};
|
||||
const res = await api.UploadCert(req);
|
||||
if (opts.onSubmit) {
|
||||
await opts.onSubmit(res);
|
||||
await opts.onSubmit(form);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
|
||||
import { checkPipelineLimit, readCertDetail } from "/@/views/certd/pipeline/utils";
|
||||
import { omit } from "lodash-es";
|
||||
import * as api from "/@/views/certd/pipeline/api";
|
||||
import { message } from "ant-design-vue";
|
||||
|
@ -52,6 +52,7 @@ export function useCertd(certdFormRef: any) {
|
|||
await checkPipelineLimit();
|
||||
|
||||
certdFormRef.value.open(async ({ form }: any) => {
|
||||
const certDetail = readCertDetail(form.cert.crt);
|
||||
// 添加certd pipeline
|
||||
const triggers = [];
|
||||
if (form.triggerCron) {
|
||||
|
|
|
@ -22,6 +22,7 @@ const props = defineProps<{
|
|||
form: any;
|
||||
input: any;
|
||||
pluginName: string;
|
||||
stepId: string;
|
||||
}>();
|
||||
|
||||
const attrs = useAttrs();
|
||||
|
@ -85,7 +86,7 @@ const doPluginFormSubmit = async (formData: any) => {
|
|||
|
||||
if (res.input) {
|
||||
const { save, findStep } = getPipelineScope();
|
||||
const step = findStep(res.input);
|
||||
const step = findStep(props.stepId);
|
||||
if (step) {
|
||||
// 数组覆盖合并
|
||||
mergeWith(step.input, res.input, (objValue, srcValue) => {
|
||||
|
|
|
@ -41,6 +41,7 @@ async function init() {
|
|||
...shortcut,
|
||||
pluginName: stepType,
|
||||
input: step.input,
|
||||
stepId: step.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
</template>
|
||||
<span class="flex-o w-100">
|
||||
<span class="ellipsis flex-1 task-title" :class="{ 'in-edit': editMode, deleted: task.disabled }">{{ task.title }}</span>
|
||||
<pi-status-show :status="task.status?.result"></pi-status-show>
|
||||
<pi-status-show v-if="!editMode" :status="task.status?.result"></pi-status-show>
|
||||
</span>
|
||||
</a-popover>
|
||||
</a-button>
|
||||
|
@ -273,6 +273,7 @@ import { FsIcon } from "@fast-crud/fast-crud";
|
|||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import TaskShortcuts from "./component/shortcut/task-shortcuts.vue";
|
||||
import { eachSteps, findStep } from "../utils";
|
||||
export default defineComponent({
|
||||
name: "PipelineEdit",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
|
@ -648,22 +649,6 @@ export default defineComponent({
|
|||
errors.push(error);
|
||||
}
|
||||
|
||||
function eachSteps(pp: any, callback: any) {
|
||||
if (pp.stages) {
|
||||
for (const stage of pp.stages) {
|
||||
if (stage.tasks) {
|
||||
for (const task of stage.tasks) {
|
||||
if (task.steps) {
|
||||
for (const step of task.steps) {
|
||||
callback(step, task, stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doValidate() {
|
||||
validateErrors.value = {};
|
||||
|
||||
|
@ -748,15 +733,8 @@ export default defineComponent({
|
|||
toggleEditMode(false);
|
||||
};
|
||||
|
||||
function findStep(id: string) {
|
||||
let found = null;
|
||||
const pp = pipeline.value;
|
||||
eachSteps(pp, (step: any, task: any, stage: any) => {
|
||||
if (step.id === id) {
|
||||
found = step;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
function fundStepFromPipeline(id: string) {
|
||||
return findStep(pipeline.value, id);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -766,7 +744,7 @@ export default defineComponent({
|
|||
cancel,
|
||||
saveLoading,
|
||||
hasValidateError,
|
||||
findStep,
|
||||
findStep: fundStepFromPipeline,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ import { forEach } from "lodash-es";
|
|||
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
|
||||
//@ts-ignore
|
||||
import forge from "node-forge";
|
||||
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
|
||||
if (!list || list.length <= 0) {
|
||||
return;
|
||||
|
@ -17,6 +18,42 @@ export function eachStages(list: any[], exec: (item: any, runnableType: string)
|
|||
});
|
||||
}
|
||||
|
||||
export function eachSteps(pipeline: any, callback: any) {
|
||||
const pp = pipeline;
|
||||
if (pp.stages) {
|
||||
for (const stage of pp.stages) {
|
||||
if (stage.tasks) {
|
||||
for (const task of stage.tasks) {
|
||||
if (task.steps) {
|
||||
for (const step of task.steps) {
|
||||
callback(step, task, stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function findStep(pipeline: any, id: string) {
|
||||
const pp = pipeline;
|
||||
if (pp.stages) {
|
||||
for (const stage of pp.stages) {
|
||||
if (stage.tasks) {
|
||||
for (const task of stage.tasks) {
|
||||
if (task.steps) {
|
||||
for (const step of task.steps) {
|
||||
if (step.id === id) {
|
||||
return step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkPipelineLimit() {
|
||||
const settingsStore = useSettingStore();
|
||||
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
|
||||
|
@ -32,3 +69,34 @@ export async function checkPipelineLimit() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function readCertDetail(crt: string) {
|
||||
const detail = forge.pki.certificateFromPem(crt);
|
||||
const expires = detail.notAfter;
|
||||
return { detail, expires };
|
||||
}
|
||||
|
||||
export function getAllDomainsFromCrt(crt: string) {
|
||||
const { detail } = readCertDetail(crt);
|
||||
const domains = [];
|
||||
|
||||
// 1. 提取SAN中的DNS名称
|
||||
const sanExtension = detail.extensions.find((ext: any) => ext.name === "subjectAltName");
|
||||
if (sanExtension) {
|
||||
sanExtension.altNames.forEach((altName: any) => {
|
||||
if (altName.type === 2) {
|
||||
// type=2 表示DNS名称
|
||||
domains.push(altName.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 如果没有SAN,回退到CN(通用名称)
|
||||
if (domains.length === 0) {
|
||||
const cnAttr = detail.subject.attributes.find((attr: any) => attr.name === "commonName");
|
||||
if (cnAttr) {
|
||||
domains.push(cnAttr.value);
|
||||
}
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import { Constants, CrudController } from '@certd/lib-server';
|
||||
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js';
|
||||
import { CertInfoService } from '../../../modules/monitor/index.js';
|
||||
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
|
||||
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||
import { CommonException, Constants, CrudController } from "@certd/lib-server";
|
||||
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
||||
import { CertInfoService } from "../../../modules/monitor/index.js";
|
||||
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
|
||||
import { SelectQueryBuilder } from "typeorm";
|
||||
import { CertUploadService } from "../../../modules/monitor/service/cert-upload-service.js";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
import { logger } from "@certd/basic";
|
||||
import fs from "fs";
|
||||
|
||||
/**
|
||||
*/
|
||||
|
@ -17,8 +17,6 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
|||
@Inject()
|
||||
authService: AuthService;
|
||||
@Inject()
|
||||
certUploadService: CertUploadService;
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
|
||||
getService(): CertInfoService {
|
||||
|
@ -131,26 +129,28 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
|||
return this.ok(certInfo);
|
||||
}
|
||||
|
||||
@Post('/upload', { summary: Constants.per.authOnly })
|
||||
async upload(@Body(ALL) body: {cert: CertInfo, pipeline: any, id?: number}) {
|
||||
if (body.id) {
|
||||
//修改
|
||||
await this.service.checkUserId(body.id, this.getUserId());
|
||||
await this.certUploadService.updateCert({
|
||||
id: body.id,
|
||||
userId: this.getUserId(),
|
||||
cert: body.cert,
|
||||
});
|
||||
return this.ok();
|
||||
}else{
|
||||
//添加
|
||||
const res = await this.certUploadService.createUploadCertPipeline({
|
||||
userId: this.getUserId(),
|
||||
cert: body.cert,
|
||||
pipeline: body.pipeline,
|
||||
});
|
||||
return this.ok(res)
|
||||
@Get('/download', { summary: Constants.per.authOnly })
|
||||
async download(@Query('id') id: number) {
|
||||
const certInfo = await this.service.info(id)
|
||||
if (certInfo == null) {
|
||||
throw new CommonException('file not found');
|
||||
}
|
||||
if (certInfo.userId !== this.getUserId()) {
|
||||
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');
|
||||
}
|
||||
logger.info(`download:${path}`);
|
||||
// 以流的形式下载文件
|
||||
this.ctx.attachment(path);
|
||||
this.ctx.set('Content-Type', 'application/octet-stream');
|
||||
|
||||
return fs.createReadStream(path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
import {EmailService} from '../../../modules/basic/service/email-service.js';
|
||||
import {http, HttpRequestConfig, logger, mergeUtils, utils} from '@certd/basic';
|
||||
import {NotificationService} from '../../../modules/pipeline/service/notification-service.js';
|
||||
import {CertApplyUploadService} from "../../../modules/pipeline/service/cert-apply-upload-service.js";
|
||||
|
||||
@Provide()
|
||||
@Controller('/api/pi/handle')
|
||||
|
@ -24,8 +23,6 @@ export class HandleController extends BaseController {
|
|||
@Inject()
|
||||
emailService: EmailService;
|
||||
|
||||
@Inject()
|
||||
certApplyUploadService: CertApplyUploadService;
|
||||
|
||||
@Inject()
|
||||
notificationService: NotificationService;
|
||||
|
@ -97,7 +94,6 @@ export class HandleController extends BaseController {
|
|||
};
|
||||
|
||||
const serviceContainer:any = {
|
||||
CertApplyUploadService:this.certApplyUploadService
|
||||
}
|
||||
const serviceGetter = {
|
||||
get:(name: string) => {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { CertInfoService } from '../monitor/index.js';
|
||||
import { pipelineEmitter } from '@certd/pipeline';
|
||||
import { CertReader, EVENT_CERT_APPLY_SUCCESS } from '@certd/plugin-cert';
|
||||
import { PipelineEvent } from '@certd/pipeline/dist/service/emit.js';
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { CertInfoService } from "../monitor/index.js";
|
||||
import { pipelineEmitter } from "@certd/pipeline";
|
||||
import { CertInfo, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-cert";
|
||||
import { PipelineEvent } from "@certd/pipeline/dist/service/emit.js";
|
||||
|
||||
@Autoload()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
|
@ -15,8 +15,8 @@ export class AutoEPipelineEmitterRegister {
|
|||
await this.onCertApplySuccess();
|
||||
}
|
||||
async onCertApplySuccess() {
|
||||
pipelineEmitter.on(EVENT_CERT_APPLY_SUCCESS, async (event: PipelineEvent<CertReader>) => {
|
||||
await this.certInfoService.updateCertByPipelineId(event.pipeline.id, event.event);
|
||||
pipelineEmitter.on(EVENT_CERT_APPLY_SUCCESS, async (event: PipelineEvent<{cert:CertInfo,file:string}>) => {
|
||||
await this.certInfoService.updateCertByPipelineId(event.pipeline.id, event.event.cert, event.event.file);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export type UploadCertReq = {
|
|||
certReader: CertReader;
|
||||
fromType?: string;
|
||||
userId?: number;
|
||||
file?:any
|
||||
};
|
||||
|
||||
|
||||
|
@ -38,7 +39,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
|||
});
|
||||
}
|
||||
|
||||
async updateDomains(pipelineId: number, userId: number, domains: string[]) {
|
||||
async updateDomains(pipelineId: number, userId: number, domains: string[],fromType?:string) {
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
pipelineId,
|
||||
|
@ -53,6 +54,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
|||
//create
|
||||
bean.pipelineId = pipelineId;
|
||||
bean.userId = userId;
|
||||
bean.fromType = fromType
|
||||
if (!domains || domains.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -133,7 +135,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
|||
return certReader.toCertInfo();
|
||||
}
|
||||
|
||||
async updateCertByPipelineId(pipelineId: number, certReader: CertReader, fromType = 'pipeline') {
|
||||
async updateCertByPipelineId(pipelineId: number, cert: CertInfo,file?:string,fromType = 'pipeline') {
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
pipelineId,
|
||||
|
@ -141,8 +143,9 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
|||
});
|
||||
const bean = await this.updateCert({
|
||||
id: found?.id,
|
||||
certReader,
|
||||
certReader: new CertReader(cert),
|
||||
fromType,
|
||||
file
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
@ -165,6 +168,9 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
|||
bean.expiresTime = certReader.expires;
|
||||
bean.certProvider = certReader.detail.issuer.commonName;
|
||||
bean.userId = userId
|
||||
if(req.file){
|
||||
bean.certFile = req.file
|
||||
}
|
||||
await this.addOrUpdate(bean);
|
||||
return bean;
|
||||
}
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
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 { 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;
|
||||
pipelineId?: number;
|
||||
};
|
||||
|
||||
export type UpdateCertReq = {
|
||||
id: number;
|
||||
cert: CertInfo;
|
||||
userId?: number;
|
||||
};
|
||||
|
||||
export type CreateUploadPipelineReq = {
|
||||
cert: CertInfo;
|
||||
userId: number;
|
||||
pipelineId?: number;
|
||||
pipeline?:{
|
||||
input?:any;
|
||||
notifications?:any[]
|
||||
}
|
||||
};
|
||||
|
||||
@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)
|
||||
})
|
||||
|
||||
return {
|
||||
id: certInfoEntity.id,
|
||||
domains: certInfoEntity.domains.split(','),
|
||||
pipelineId: certInfoEntity.pipelineId,
|
||||
fromType: certInfoEntity.fromType,
|
||||
updateTime: certInfoEntity.updateTime,
|
||||
}
|
||||
}
|
||||
|
||||
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)=>{
|
||||
let pipelineId = body.pipelineId;
|
||||
const newCertInfo = await this.uploadCert(tx,{
|
||||
certReader: certReader,
|
||||
fromType: 'upload',
|
||||
userId,
|
||||
pipelineId
|
||||
});
|
||||
|
||||
if(!pipelineId){
|
||||
const pipelineTitle = certReader.getAllDomains()[0] +"上传证书自动部署";
|
||||
const notifications = body.pipeline?.notifications ||[];
|
||||
if(notifications.length === 0){
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: 0,
|
||||
title: "默认通知",
|
||||
});
|
||||
}
|
||||
|
||||
let pipeline = {
|
||||
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(','),
|
||||
...body.pipeline?.input
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0, // 正常执行
|
||||
},
|
||||
type: "CertApplyUpload",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
triggers:[],
|
||||
notifications,
|
||||
}
|
||||
const newPipeline = await tx.getRepository(PipelineEntity).save({
|
||||
userId,
|
||||
title: pipelineTitle,
|
||||
type:"cert_upload",
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistory:20,
|
||||
})
|
||||
|
||||
newCertInfo.pipelineId = newPipeline.id;
|
||||
await tx.getRepository(CertInfoEntity).save({
|
||||
id: newCertInfo.id,
|
||||
pipelineId: newPipeline.id
|
||||
});
|
||||
|
||||
pipelineId = newPipeline.id;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
id:newCertInfo.id,
|
||||
pipelineId: pipelineId,
|
||||
domains: newCertInfo.domains.split(','),
|
||||
fromType: newCertInfo.fromType,
|
||||
updateTime: newCertInfo.updateTime,
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
if (req.pipelineId){
|
||||
bean.pipelineId = req.pipelineId;
|
||||
}
|
||||
|
||||
await tx.getRepository(CertInfoEntity).save(bean);
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import {ICertApplyUploadService} from "@certd/plugin-cert";
|
||||
import {IMidwayContext, Inject, Provide} from "@midwayjs/core";
|
||||
import {CertInfoService} from "../../monitor/index.js";
|
||||
import {CertUploadService} from "../../monitor/service/cert-upload-service.js";
|
||||
|
||||
@Provide("CertApplyUploadService")
|
||||
export class CertApplyUploadService implements ICertApplyUploadService {
|
||||
@Inject()
|
||||
ctx : IMidwayContext
|
||||
|
||||
async getCertInfo(opts: { certId: number; userId: number; }) {
|
||||
const certInfoService = this.ctx.getApp().getApplicationContext().get<CertInfoService>("CertInfoService")
|
||||
const { certId, userId } = opts;
|
||||
return await certInfoService.getCertInfo({
|
||||
certId,
|
||||
userId: Number(userId),
|
||||
});
|
||||
};
|
||||
async updateCert(opts: { certId: any; userId: any; cert: any; }){
|
||||
const certUploadService = this.ctx.getApp().getApplicationContext().get<CertUploadService>("CertUploadService")
|
||||
return await certUploadService.updateCert({
|
||||
id:opts.certId,
|
||||
userId:opts.userId,
|
||||
cert:opts.cert
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,6 @@ import { NotificationService } from "./notification-service.js";
|
|||
import { NotificationGetter } from "./notification-getter.js";
|
||||
import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
|
||||
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
|
||||
import {CertApplyUploadService} from "./cert-apply-upload-service.js";
|
||||
|
||||
const runningTasks: Map<string | number, Executor> = new Map();
|
||||
|
||||
|
@ -93,8 +92,6 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
@Inject()
|
||||
certApplyUploadService: CertApplyUploadService;
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
|
@ -196,10 +193,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
await this.registerTriggerById(bean.id);
|
||||
|
||||
//保存域名信息到certInfo表
|
||||
if(bean.from !== 'cert_upload'){
|
||||
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains);
|
||||
let fromType = 'pipeline';
|
||||
if(bean.type === 'cert_upload') {
|
||||
fromType = 'upload';
|
||||
}
|
||||
|
||||
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains,fromType);
|
||||
return bean;
|
||||
}
|
||||
|
||||
|
@ -483,9 +481,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
|
||||
sysInfo.title = siteInfo.title;
|
||||
}
|
||||
const serviceContainer = {
|
||||
CertApplyUploadService: this.certApplyUploadService
|
||||
}
|
||||
const serviceContainer = {}
|
||||
const serviceGetter = {
|
||||
get:(name: string) => {
|
||||
return serviceContainer[name]
|
||||
|
|
|
@ -1072,6 +1072,9 @@ importers:
|
|||
cropperjs:
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.2
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
cssnano:
|
||||
specifier: ^7.0.6
|
||||
version: 7.0.6(postcss@8.5.3)
|
||||
|
@ -1102,6 +1105,9 @@ importers:
|
|||
nanoid:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.2
|
||||
node-forge:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
nprogress:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
|
|
Loading…
Reference in New Issue