mirror of https://github.com/certd/certd
chore: 支持手动上传证书并部署
parent
873f2b618b
commit
d1b61b6bf9
|
@ -11,11 +11,13 @@ function attachProperty(target: any, propertyKey: string | symbol) {
|
|||
}
|
||||
|
||||
function getClassProperties(target: any) {
|
||||
//获取父类
|
||||
//获取父类, 向上追溯三层
|
||||
const parent = Object.getPrototypeOf(target);
|
||||
const pParent = Object.getPrototypeOf(parent);
|
||||
const pParentMap = propertyMap[pParent] || {};
|
||||
const parentMap = propertyMap[parent] || {};
|
||||
const current = propertyMap[target] || {};
|
||||
return _.merge({}, parentMap, current);
|
||||
return _.merge({}, pParentMap, parentMap, current);
|
||||
}
|
||||
|
||||
function target(target: any, propertyKey?: string | symbol) {
|
||||
|
|
|
@ -30,7 +30,7 @@ export abstract class BaseService<T> {
|
|||
|
||||
async transaction(callback: (entityManager: EntityManager) => Promise<any>) {
|
||||
const dataSource = this.dataSourceManager.getDataSource('default');
|
||||
await dataSource.transaction(callback as any);
|
||||
return await dataSource.transaction(callback as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,14 +31,14 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
|||
domains!: string[];
|
||||
|
||||
@TaskInput({
|
||||
title: "证书密码",
|
||||
title: "证书加密密码",
|
||||
component: {
|
||||
name: "input-password",
|
||||
vModel: "value",
|
||||
},
|
||||
required: false,
|
||||
order: 100,
|
||||
helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456\npfx不传则为空密码",
|
||||
helper: "转换成PFX、jks格式证书是否需要加密\njks必须设置密码,不传则默认123456\npfx不传则为空密码",
|
||||
})
|
||||
pfxPassword!: string;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ export async function emitCertApplySuccess(emitter: TaskEmitter, cert: CertReade
|
|||
}
|
||||
|
||||
export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
||||
|
||||
@TaskInput({
|
||||
title: "邮箱",
|
||||
component: {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<div class="file-input">
|
||||
<a-button :type="type" @click="onClick">{{ text }}</a-button> {{ fileName }}
|
||||
<div class="hidden">
|
||||
<input ref="fileInputRef" type="file" @change="onFileChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineEmits, defineProps } from "vue";
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const props = defineProps<{
|
||||
text: string;
|
||||
type: string;
|
||||
}>();
|
||||
const fileName = ref("");
|
||||
const emit = defineEmits(["change"]);
|
||||
function onClick() {
|
||||
fileInputRef.value.click();
|
||||
}
|
||||
function onFileChange(e: any) {
|
||||
fileName.value = e.target.files[0].name;
|
||||
emit("change", e);
|
||||
}
|
||||
</script>
|
|
@ -10,10 +10,12 @@ import Plugins from "./plugins/index";
|
|||
import LoadingButton from "./loading-button.vue";
|
||||
import IconSelect from "./icon-select.vue";
|
||||
import ExpiresTimeText from "./expires-time-text.vue";
|
||||
import FileInput from "./file-input.vue";
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.component("PiContainer", PiContainer);
|
||||
app.component("TextEditable", TextEditable);
|
||||
app.component("FileInput", FileInput);
|
||||
|
||||
app.component("CronLight", CronLight);
|
||||
app.component("CronEditor", CronEditor);
|
||||
|
@ -29,5 +31,5 @@ export default {
|
|||
app.component("ExpiresTimeText", ExpiresTimeText);
|
||||
app.use(vip);
|
||||
app.use(Plugins);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@ const apiPrefix = "/pi/plugin";
|
|||
const defaultInputDefine = {
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "modelValue"
|
||||
}
|
||||
vModel: "modelValue",
|
||||
},
|
||||
};
|
||||
|
||||
function initPlugins(plugins: any) {
|
||||
|
@ -35,7 +35,7 @@ export async function GetList(query: any) {
|
|||
const plugins = await request({
|
||||
url: apiPrefix + "/list",
|
||||
method: "post",
|
||||
params: query
|
||||
params: query,
|
||||
});
|
||||
initPlugins(plugins);
|
||||
return plugins;
|
||||
|
@ -45,7 +45,7 @@ export async function GetGroups(query: any) {
|
|||
const groups = await request({
|
||||
url: apiPrefix + "/groups",
|
||||
method: "post",
|
||||
params: query
|
||||
params: query,
|
||||
});
|
||||
const plugins: any = [];
|
||||
for (const groupKey in groups) {
|
||||
|
@ -60,8 +60,8 @@ export async function GetPluginDefine(type: string) {
|
|||
url: apiPrefix + "/getDefineByType",
|
||||
method: "post",
|
||||
data: {
|
||||
type
|
||||
}
|
||||
type,
|
||||
},
|
||||
});
|
||||
initPlugins([define]);
|
||||
return define;
|
||||
|
@ -71,6 +71,6 @@ export async function GetPluginConfig(req: { id?: number; name: string; type: st
|
|||
return await request({
|
||||
url: apiPrefix + "/config",
|
||||
method: "post",
|
||||
data: req
|
||||
data: req,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/monitor/cert";
|
||||
export async function UploadCert(body: { id?: number; cert: { crt: string; key: string }; pipeline?: any }) {
|
||||
return await request({
|
||||
url: apiPrefix + "/upload",
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
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 { 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 { useRouter } from "vue-router";
|
||||
|
||||
export function useCertUpload() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const router = useRouter();
|
||||
|
||||
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") {
|
||||
continue;
|
||||
}
|
||||
const inputDefine = cloneDeep(plugin.input[inputKey]);
|
||||
useReference(inputDefine);
|
||||
inputs[inputKey] = {
|
||||
title: inputDefine.title,
|
||||
form: {
|
||||
...inputDefine,
|
||||
show: compute(ctx => {
|
||||
const form = getFormData();
|
||||
if (!form) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let inputDefineShow = true;
|
||||
if (inputDefine.show != null) {
|
||||
const computeShow = inputDefine.show as any;
|
||||
if (computeShow === false) {
|
||||
inputDefineShow = false;
|
||||
} else if (computeShow && computeShow.computeFn) {
|
||||
inputDefineShow = computeShow.computeFn({ form });
|
||||
}
|
||||
}
|
||||
return inputDefineShow;
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
function topRender({ form, key }: any) {
|
||||
function onChange(e: any) {
|
||||
const file = e.target.files[0];
|
||||
const size = file.size;
|
||||
if (size > 100 * 1024) {
|
||||
notification.error({
|
||||
message: "文件超过100k,请选择正确的证书文件",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = function (e: any) {
|
||||
const value = e.target.result;
|
||||
set(form, key, value);
|
||||
};
|
||||
fileReader.readAsText(file); // 以文本形式读取文件
|
||||
}
|
||||
return <file-input class="mb-5" type="primary" text={"选择文件"} onChange={onChange} />;
|
||||
}
|
||||
|
||||
async function openUploadCreateDialog() {
|
||||
//检查是否流水线数量超出限制
|
||||
await checkPipelineLimit();
|
||||
|
||||
const wrapperRef = ref();
|
||||
function getFormData() {
|
||||
if (!wrapperRef.value) {
|
||||
return null;
|
||||
}
|
||||
return wrapperRef.value.getFormData();
|
||||
}
|
||||
const inputs = await buildUploadCertPluginInputs(getFormData);
|
||||
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
"cert.crt": {
|
||||
title: "证书",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN CERTIFICATE-----\n...\n...\n-----END CERTIFICATE-----",
|
||||
},
|
||||
helper: "选择pem格式证书文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
topRender,
|
||||
},
|
||||
},
|
||||
"cert.key": {
|
||||
title: "证书私钥",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: {
|
||||
rows: 4,
|
||||
placeholder: "-----BEGIN PRIVATE KEY-----\n...\n...\n-----END PRIVATE KEY----- ",
|
||||
},
|
||||
helper: "选择pem格式证书私钥文件,或者粘贴到此",
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
order: -9999,
|
||||
topRender,
|
||||
},
|
||||
},
|
||||
...inputs,
|
||||
notification: {
|
||||
title: "失败通知",
|
||||
type: "text",
|
||||
form: {
|
||||
value: 0,
|
||||
component: {
|
||||
name: NotificationSelector,
|
||||
vModel: "modelValue",
|
||||
on: {
|
||||
selectedChange({ $event, form }: any) {
|
||||
form.notificationTarget = $event;
|
||||
},
|
||||
},
|
||||
},
|
||||
order: 101,
|
||||
helper: "任务执行失败实时提醒",
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
title: "上传证书&创建部署流水线",
|
||||
saveRemind: false,
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
const notifications = [];
|
||||
if (form.notification != null) {
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: form.notification,
|
||||
title: form.notificationTarget?.name || "自定义通知",
|
||||
});
|
||||
}
|
||||
|
||||
const req = {
|
||||
id: form.id,
|
||||
cert: form.cert,
|
||||
pipeline: {
|
||||
input: omit(form, ["id", "cert", "notification", "notificationTarget"]),
|
||||
notifications,
|
||||
},
|
||||
};
|
||||
const res = await api.UploadCert(req);
|
||||
router.push({
|
||||
path: "/certd/pipeline/detail",
|
||||
query: { id: res.pipelineId, editMode: "true" },
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
const wrapper = await openCrudFormDialog({ crudOptions });
|
||||
wrapperRef.value = wrapper;
|
||||
}
|
||||
|
||||
async function openUpdateCertDialog(id: any) {
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
"cert.crt": {
|
||||
title: "证书",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: {
|
||||
rows: 4,
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
},
|
||||
},
|
||||
"cert.key": {
|
||||
title: "私钥",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: {
|
||||
rows: 4,
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
col: { span: 24 },
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
title: "更新证书",
|
||||
saveRemind: false,
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
const req = {
|
||||
id: id,
|
||||
cert: form.cert,
|
||||
};
|
||||
return await api.UploadCert(form);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
await openCrudFormDialog({ crudOptions });
|
||||
}
|
||||
|
||||
return {
|
||||
openUploadCreateDialog,
|
||||
openUpdateCertDialog,
|
||||
};
|
||||
}
|
|
@ -14,7 +14,7 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
|
|||
for (const plugin of certPlugins) {
|
||||
for (const inputKey in plugin.input) {
|
||||
if (inputs[inputKey]) {
|
||||
inputs[inputKey].form.show = true;
|
||||
// inputs[inputKey].form.show = true;
|
||||
continue;
|
||||
}
|
||||
const inputDefine = _.cloneDeep(plugin.input[inputKey]);
|
||||
|
|
|
@ -4,11 +4,11 @@ export const Dicts = {
|
|||
sslProviderDict: dict({
|
||||
data: [
|
||||
{ value: "letsencrypt", label: "Let‘s Encrypt" },
|
||||
{ value: "zerossl", label: "ZeroSSL" }
|
||||
]
|
||||
{ value: "zerossl", label: "ZeroSSL" },
|
||||
],
|
||||
}),
|
||||
challengeTypeDict: dict({ data: [{ value: "dns", label: "DNS校验" }] }),
|
||||
dnsProviderTypeDict: dict({
|
||||
url: "pi/dnsProvider/dnsProviderTypeDict"
|
||||
})
|
||||
url: "pi/dnsProvider/dnsProviderTypeDict",
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -44,8 +44,8 @@ export default {
|
|||
const notificationApi = createNotificationApi();
|
||||
await notificationApi.GetOrCreateDefault({ email: form.email });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}) as any
|
||||
);
|
||||
|
||||
|
@ -60,9 +60,9 @@ export default {
|
|||
return {
|
||||
formWrapperRef,
|
||||
open,
|
||||
formWrapperOptions
|
||||
formWrapperOptions,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
|
||||
import { omit } from "lodash-es";
|
||||
import * as api from "/@/views/certd/pipeline/api";
|
||||
import { message } from "ant-design-vue";
|
||||
import { nanoid } from "nanoid";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
export function setRunnableIds(pipeline: any) {
|
||||
const idMap: any = {};
|
||||
function createId(oldId: any) {
|
||||
if (oldId == null) {
|
||||
return nanoid();
|
||||
}
|
||||
const newId = nanoid();
|
||||
idMap[oldId] = newId;
|
||||
return newId;
|
||||
}
|
||||
if (pipeline.stages) {
|
||||
for (const stage of pipeline.stages) {
|
||||
stage.id = createId(stage.id);
|
||||
if (stage.tasks) {
|
||||
for (const task of stage.tasks) {
|
||||
task.id = createId(task.id);
|
||||
if (task.steps) {
|
||||
for (const step of task.steps) {
|
||||
step.id = createId(step.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const trigger of pipeline.triggers) {
|
||||
trigger.id = nanoid();
|
||||
}
|
||||
for (const notification of pipeline.notifications) {
|
||||
notification.id = nanoid();
|
||||
}
|
||||
|
||||
let content = JSON.stringify(pipeline);
|
||||
for (const key in idMap) {
|
||||
content = content.replaceAll(key, idMap[key]);
|
||||
}
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
export function useCertd(certdFormRef: any) {
|
||||
const router = useRouter();
|
||||
async function openAddCertdPipelineDialog() {
|
||||
//检查是否流水线数量超出限制
|
||||
await checkPipelineLimit();
|
||||
|
||||
certdFormRef.value.open(async ({ form }: any) => {
|
||||
// 添加certd pipeline
|
||||
const triggers = [];
|
||||
if (form.triggerCron) {
|
||||
triggers.push({ title: "定时触发", type: "timer", props: { cron: form.triggerCron } });
|
||||
}
|
||||
const notifications = [];
|
||||
if (form.notification != null) {
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: form.notification,
|
||||
title: form.notificationTarget?.name || "自定义通知",
|
||||
});
|
||||
}
|
||||
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin"]);
|
||||
let pipeline = {
|
||||
title: form.domains[0] + "证书自动化",
|
||||
runnableType: "pipeline",
|
||||
stages: [
|
||||
{
|
||||
title: "证书申请阶段",
|
||||
maxTaskCount: 1,
|
||||
runnableType: "stage",
|
||||
tasks: [
|
||||
{
|
||||
title: "证书申请任务",
|
||||
runnableType: "task",
|
||||
steps: [
|
||||
{
|
||||
title: "申请证书",
|
||||
runnableType: "step",
|
||||
input: {
|
||||
renewDays: 35,
|
||||
...pluginInput,
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0, // 正常执行
|
||||
},
|
||||
type: form.certApplyPlugin,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
triggers,
|
||||
notifications,
|
||||
};
|
||||
pipeline = setRunnableIds(pipeline);
|
||||
|
||||
/**
|
||||
* // cert: 证书; backup: 备份; custom:自定义;
|
||||
* type: string;
|
||||
* // custom: 自定义; monitor: 监控;
|
||||
* from: string;
|
||||
*/
|
||||
const id = await api.Save({
|
||||
title: pipeline.title,
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistoryCount: 30,
|
||||
type: "cert",
|
||||
from: "custom",
|
||||
});
|
||||
message.success("创建成功,请添加证书部署任务");
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
openAddCertdPipelineDialog,
|
||||
};
|
||||
}
|
|
@ -4,62 +4,26 @@ import { computed, ref } from "vue";
|
|||
import { useRouter } from "vue-router";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
|
||||
import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status";
|
||||
import { nanoid } from "nanoid";
|
||||
import { message, Modal, notification } from "ant-design-vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { env } from "/@/utils/util.env";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import dayjs from "dayjs";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import * as _ from "lodash-es";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { useModal } from "/@/use/use-modal";
|
||||
import CertView from "./cert-view.vue";
|
||||
import { eachStages } from "./utils";
|
||||
import { createNotificationApi as createNotificationApi } from "../notification/api";
|
||||
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
||||
import { setRunnableIds, useCertd } from "/@/views/certd/pipeline/certd-form/use";
|
||||
import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use";
|
||||
|
||||
export default function ({ crudExpose, context: { certdFormRef, groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const lastResRef = ref();
|
||||
|
||||
function setRunnableIds(pipeline: any) {
|
||||
const idMap: any = {};
|
||||
function createId(oldId: any) {
|
||||
if (oldId == null) {
|
||||
return nanoid();
|
||||
}
|
||||
const newId = nanoid();
|
||||
idMap[oldId] = newId;
|
||||
return newId;
|
||||
}
|
||||
if (pipeline.stages) {
|
||||
for (const stage of pipeline.stages) {
|
||||
stage.id = createId(stage.id);
|
||||
if (stage.tasks) {
|
||||
for (const task of stage.tasks) {
|
||||
task.id = createId(task.id);
|
||||
if (task.steps) {
|
||||
for (const step of task.steps) {
|
||||
step.id = createId(step.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const { openAddCertdPipelineDialog } = useCertd(certdFormRef);
|
||||
const { openUploadCreateDialog } = useCertUpload();
|
||||
|
||||
for (const trigger of pipeline.triggers) {
|
||||
trigger.id = nanoid();
|
||||
}
|
||||
for (const notification of pipeline.notifications) {
|
||||
notification.id = nanoid();
|
||||
}
|
||||
|
||||
let content = JSON.stringify(pipeline);
|
||||
for (const key in idMap) {
|
||||
content = content.replaceAll(key, idMap[key]);
|
||||
}
|
||||
return JSON.parse(content);
|
||||
}
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
|
@ -96,90 +60,6 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
|||
return res;
|
||||
};
|
||||
|
||||
const settingsStore = useSettingStore();
|
||||
async function addCertdPipeline() {
|
||||
//检查是否流水线数量超出限制
|
||||
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
|
||||
//检查数量是否超限
|
||||
|
||||
const suiteDetail = await mySuiteApi.SuiteDetailGet();
|
||||
const max = suiteDetail.pipelineCount.max;
|
||||
if (max != -1 && max <= suiteDetail.pipelineCount.used) {
|
||||
notification.error({
|
||||
message: `对不起,您最多只能创建${max}条流水线,请购买或升级套餐`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
certdFormRef.value.open(async ({ form }: any) => {
|
||||
// 添加certd pipeline
|
||||
const triggers = [];
|
||||
if (form.triggerCron) {
|
||||
triggers.push({ title: "定时触发", type: "timer", props: { cron: form.triggerCron } });
|
||||
}
|
||||
const notifications = [];
|
||||
if (form.notification != null) {
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: form.notification,
|
||||
title: form.notificationTarget?.name || "自定义通知",
|
||||
});
|
||||
}
|
||||
let pipeline = {
|
||||
title: form.domains[0] + "证书自动化",
|
||||
runnableType: "pipeline",
|
||||
stages: [
|
||||
{
|
||||
title: "证书申请阶段",
|
||||
maxTaskCount: 1,
|
||||
runnableType: "stage",
|
||||
tasks: [
|
||||
{
|
||||
title: "证书申请任务",
|
||||
runnableType: "task",
|
||||
steps: [
|
||||
{
|
||||
title: "申请证书",
|
||||
runnableType: "step",
|
||||
input: {
|
||||
renewDays: 35,
|
||||
...form,
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0, // 正常执行
|
||||
},
|
||||
type: form.certApplyPlugin,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
triggers,
|
||||
notifications,
|
||||
};
|
||||
pipeline = setRunnableIds(pipeline);
|
||||
|
||||
/**
|
||||
* // cert: 证书; backup: 备份; custom:自定义;
|
||||
* type: string;
|
||||
* // custom: 自定义; monitor: 监控;
|
||||
* from: string;
|
||||
*/
|
||||
const id = await api.Save({
|
||||
title: pipeline.title,
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistoryCount: 30,
|
||||
type: "cert",
|
||||
from: "custom",
|
||||
});
|
||||
message.success("创建成功,请添加证书部署任务");
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||
});
|
||||
}
|
||||
|
||||
const model = useModal();
|
||||
const viewCert = async (row: any) => {
|
||||
const cert = await api.GetCert(row.id);
|
||||
|
@ -268,14 +148,25 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
|||
buttons: {
|
||||
add: {
|
||||
order: 5,
|
||||
icon: "ion:ios-add-circle-outline",
|
||||
text: "自定义流水线",
|
||||
},
|
||||
addCertd: {
|
||||
order: 1,
|
||||
text: "创建证书流水线",
|
||||
type: "primary",
|
||||
icon: "ion:ios-add-circle-outline",
|
||||
click() {
|
||||
addCertdPipeline();
|
||||
openAddCertdPipelineDialog();
|
||||
},
|
||||
},
|
||||
uploadCert: {
|
||||
order: 2,
|
||||
text: "上传证书部署",
|
||||
type: "primary",
|
||||
icon: "ion:cloud-upload-outline",
|
||||
click() {
|
||||
openUploadCreateDialog();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -329,7 +220,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
|||
const { ui } = useUi();
|
||||
// @ts-ignore
|
||||
let row = context[ui.tableColumn.row];
|
||||
row = _.cloneDeep(row);
|
||||
row = cloneDeep(row);
|
||||
row.title = row.title + "_copy";
|
||||
await crudExpose.openCopy({
|
||||
row: row,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
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";
|
||||
|
||||
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
|
||||
if (!list || list.length <= 0) {
|
||||
return;
|
||||
}
|
||||
forEach(list, (item) => {
|
||||
forEach(list, item => {
|
||||
exec(item, runnableType);
|
||||
if (runnableType === "stage") {
|
||||
eachStages(item.tasks, exec, "task");
|
||||
|
@ -13,3 +16,19 @@ export function eachStages(list: any[], exec: (item: any, runnableType: string)
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkPipelineLimit() {
|
||||
const settingsStore = useSettingStore();
|
||||
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
|
||||
//检查数量是否超限
|
||||
|
||||
const suiteDetail = await mySuiteApi.SuiteDetailGet();
|
||||
const max = suiteDetail.pipelineCount.max;
|
||||
if (max != -1 && max <= suiteDetail.pipelineCount.used) {
|
||||
notification.error({
|
||||
message: `对不起,您最多只能创建${max}条流水线,请购买或升级套餐`,
|
||||
});
|
||||
throw new Error("流水线数量超限");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,16 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
|||
return this.ok(list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Post('/getCert', { summary: Constants.per.authOnly })
|
||||
async getCert(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
const certInfoEntity = await this.service.info(id);
|
||||
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
||||
return this.ok(certInfo);
|
||||
}
|
||||
|
||||
@Post('/upload', { summary: Constants.per.authOnly })
|
||||
async upload(@Body(ALL) body: {cert: CertInfo, pipeline: any, id?: number}) {
|
||||
if (body.id) {
|
||||
|
@ -131,23 +141,16 @@ export class CertInfoController extends CrudController<CertInfoService> {
|
|||
userId: this.getUserId(),
|
||||
cert: body.cert,
|
||||
});
|
||||
return this.ok();
|
||||
}else{
|
||||
//添加
|
||||
await this.certUploadService.createUploadCertPipeline({
|
||||
const res = await this.certUploadService.createUploadCertPipeline({
|
||||
userId: this.getUserId(),
|
||||
cert: body.cert,
|
||||
pipeline: body.pipeline,
|
||||
});
|
||||
|
||||
return this.ok(res)
|
||||
}
|
||||
|
||||
return this.ok();
|
||||
}
|
||||
|
||||
@Post('/getCert', { summary: Constants.per.authOnly })
|
||||
async getCert(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
const certInfoEntity = await this.service.info(id);
|
||||
const certInfo = JSON.parse(certInfoEntity.certInfo);
|
||||
return this.ok(certInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ export type UpdateCertReq = {
|
|||
export type CreateUploadPipelineReq = {
|
||||
cert: CertInfo;
|
||||
userId: number;
|
||||
pipeline?:{
|
||||
input?:any;
|
||||
notifications?:any[]
|
||||
}
|
||||
};
|
||||
|
||||
@Provide("CertUploadService")
|
||||
|
@ -86,13 +90,16 @@ export class CertUploadService extends BaseService<CertInfoEntity> {
|
|||
});
|
||||
|
||||
const pipelineTitle = certReader.getAllDomains()[0] +"上传证书自动部署";
|
||||
const notifications = [];
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
notificationId: 0,
|
||||
title: "默认通知",
|
||||
});
|
||||
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",
|
||||
|
@ -115,6 +122,7 @@ export class CertUploadService extends BaseService<CertInfoEntity> {
|
|||
input: {
|
||||
certInfoId: newCertInfo.id,
|
||||
domains: newCertInfo.domains.split(','),
|
||||
...body.pipeline?.input
|
||||
},
|
||||
strategy: {
|
||||
runStrategy: 0, // 正常执行
|
||||
|
@ -144,7 +152,10 @@ export class CertUploadService extends BaseService<CertInfoEntity> {
|
|||
pipelineId: newPipeline.id
|
||||
});
|
||||
|
||||
return newCertInfo.id
|
||||
return {
|
||||
id:newCertInfo.id,
|
||||
pipelineId: newPipeline.id
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue