mirror of
https://github.com/certd/certd.git
synced 2026-04-23 19:57:27 +08:00
perf: 手动上传证书部署流水线
This commit is contained in:
@@ -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 },
|
||||
});
|
||||
}
|
||||
|
||||
+20
-26
@@ -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) {
|
||||
|
||||
+2
-1
@@ -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) => {
|
||||
|
||||
+1
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user