mirror of https://github.com/certd/certd
perf: 优化证书流水线创建,支持选择分组
parent
5750bb7067
commit
d613aa8f3e
|
@ -7,6 +7,7 @@ import { routerUtils } from "./util.router";
|
||||||
import { treeUtils } from "./util.tree";
|
import { treeUtils } from "./util.tree";
|
||||||
import { hashUtils } from "./util.hash";
|
import { hashUtils } from "./util.hash";
|
||||||
import { amountUtils } from "./util.amount";
|
import { amountUtils } from "./util.amount";
|
||||||
|
import { cache } from "./util.cache";
|
||||||
export const util = {
|
export const util = {
|
||||||
...envs,
|
...envs,
|
||||||
...sites,
|
...sites,
|
||||||
|
@ -17,5 +18,6 @@ export const util = {
|
||||||
tree: treeUtils,
|
tree: treeUtils,
|
||||||
hash: hashUtils,
|
hash: hashUtils,
|
||||||
amount: amountUtils,
|
amount: amountUtils,
|
||||||
|
cache,
|
||||||
};
|
};
|
||||||
export const utils = util;
|
export const utils = util;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
export class Cache {
|
||||||
|
bucket: Record<string, any> = {};
|
||||||
|
|
||||||
|
async get(key: string) {
|
||||||
|
return this.bucket[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key: string, value: any, ttl: number) {
|
||||||
|
this.bucket[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async del(key: string) {
|
||||||
|
delete this.bucket[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cache = new Cache();
|
|
@ -7,7 +7,7 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/page",
|
url: apiPrefix + "/page",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: query
|
data: query,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/add",
|
url: apiPrefix + "/add",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: obj
|
data: obj,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/update",
|
url: apiPrefix + "/update",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: obj
|
data: obj,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/delete",
|
url: apiPrefix + "/delete",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
params: { id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/info",
|
url: apiPrefix + "/info",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
params: { id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async GetOptions(id: number) {
|
async GetOptions(id: number) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/options",
|
url: apiPrefix + "/options",
|
||||||
method: "post"
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -54,14 +54,14 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/setDefault",
|
url: apiPrefix + "/setDefault",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
params: { id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async GetDefaultId() {
|
async GetDefaultId() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/getDefaultId",
|
url: apiPrefix + "/getDefaultId",
|
||||||
method: "post"
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -69,14 +69,14 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/simpleInfo",
|
url: apiPrefix + "/simpleInfo",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
params: { id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async GetDefineTypes() {
|
async GetDefineTypes() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/getTypeDict",
|
url: apiPrefix + "/getTypeDict",
|
||||||
method: "post"
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/define",
|
url: apiPrefix + "/define",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { type }
|
params: { type },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -92,15 +92,15 @@ export function createNotificationApi() {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/defineByType",
|
url: apiPrefix + "/defineByType",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { type }
|
params: { type },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async GetOrCreateDefault(param: { email: any }) {
|
async GetOrCreateDefault(param: { email: any }) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/getOrCreateDefault",
|
url: apiPrefix + "/getOrCreateDefault",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: param
|
data: param,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { compute, useFormWrapper } from "@fast-crud/fast-crud";
|
import { compute, dict, useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||||
import { cloneDeep, omit } from "lodash-es";
|
import { cloneDeep, omit } from "lodash-es";
|
||||||
import { useReference } from "/@/use/use-refrence";
|
import { useReference } from "/@/use/use-refrence";
|
||||||
|
@ -90,7 +90,7 @@ export function useCertUpload() {
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openUploadCreateDialog() {
|
async function openUploadCreateDialog(req: { defaultGroupId?: number }) {
|
||||||
//检查是否流水线数量超出限制
|
//检查是否流水线数量超出限制
|
||||||
await checkPipelineLimit();
|
await checkPipelineLimit();
|
||||||
|
|
||||||
|
@ -102,7 +102,11 @@ export function useCertUpload() {
|
||||||
return wrapperRef.value.getFormData();
|
return wrapperRef.value.getFormData();
|
||||||
}
|
}
|
||||||
const inputs = await buildUploadCertPluginInputs(getFormData);
|
const inputs = await buildUploadCertPluginInputs(getFormData);
|
||||||
|
const groupDictRef = dict({
|
||||||
|
url: "/pi/pipeline/group/all",
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
});
|
||||||
function createCrudOptions() {
|
function createCrudOptions() {
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
|
@ -127,6 +131,15 @@ export function useCertUpload() {
|
||||||
helper: "任务执行失败实时提醒",
|
helper: "任务执行失败实时提醒",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
groupId: {
|
||||||
|
title: "流水线分组",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: groupDictRef,
|
||||||
|
form: {
|
||||||
|
value: req.defaultGroupId || undefined,
|
||||||
|
order: 9999,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
wrapper: {
|
wrapper: {
|
||||||
|
@ -191,6 +204,7 @@ export function useCertUpload() {
|
||||||
content: JSON.stringify(pipeline),
|
content: JSON.stringify(pipeline),
|
||||||
keepHistoryCount: 30,
|
keepHistoryCount: 30,
|
||||||
type: "cert_upload",
|
type: "cert_upload",
|
||||||
|
groupId: form.groupId,
|
||||||
});
|
});
|
||||||
router.push({
|
router.push({
|
||||||
path: "/certd/pipeline/detail",
|
path: "/certd/pipeline/detail",
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
import { compute, CreateCrudOptionsRet, dict } from "@fast-crud/fast-crud";
|
|
||||||
import { useReference } from "/@/use/use-refrence";
|
|
||||||
import { merge, cloneDeep } from "lodash-es";
|
|
||||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
|
||||||
import { usePluginStore } from "/@/store/plugin";
|
|
||||||
|
|
||||||
export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOptionsRet {
|
|
||||||
const inputs: any = {};
|
|
||||||
const moreParams = [];
|
|
||||||
for (const plugin of certPlugins) {
|
|
||||||
for (const inputKey in plugin.input) {
|
|
||||||
if (inputs[inputKey]) {
|
|
||||||
//如果两个插件有的字段,直接显示
|
|
||||||
inputs[inputKey].form.show = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const inputDefine = cloneDeep(plugin.input[inputKey]);
|
|
||||||
if (!inputDefine.required && !inputDefine.maybeNeed) {
|
|
||||||
moreParams.push(inputKey);
|
|
||||||
// continue;
|
|
||||||
}
|
|
||||||
useReference(inputDefine);
|
|
||||||
inputs[inputKey] = {
|
|
||||||
title: inputDefine.title,
|
|
||||||
form: {
|
|
||||||
...inputDefine,
|
|
||||||
show: compute(ctx => {
|
|
||||||
const form = formWrapperRef.value.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 form?.certApplyPlugin === plugin.name && inputDefineShow;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginStore = usePluginStore();
|
|
||||||
const randomHour = Math.floor(Math.random() * 6);
|
|
||||||
const randomMin = Math.floor(Math.random() * 60);
|
|
||||||
return {
|
|
||||||
crudOptions: {
|
|
||||||
form: {
|
|
||||||
wrapper: {
|
|
||||||
width: 1350,
|
|
||||||
saveRemind: false,
|
|
||||||
title: "创建证书流水线",
|
|
||||||
},
|
|
||||||
group: {
|
|
||||||
groups: {
|
|
||||||
more: {
|
|
||||||
header: "更多参数",
|
|
||||||
columns: moreParams,
|
|
||||||
collapsed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
columns: {
|
|
||||||
certApplyPlugin: {
|
|
||||||
title: "证书申请插件",
|
|
||||||
type: "dict-select",
|
|
||||||
dict: dict({
|
|
||||||
data: [
|
|
||||||
{ value: "CertApply", label: "JS-ACME" },
|
|
||||||
{ value: "CertApplyLego", label: "Lego-ACME" },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
form: {
|
|
||||||
order: 0,
|
|
||||||
value: "CertApply",
|
|
||||||
helper: {
|
|
||||||
render: () => {
|
|
||||||
return (
|
|
||||||
<ul>
|
|
||||||
<li>JS-ACME:使用简单方便,功能强大【推荐】</li>
|
|
||||||
<li>Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用</li>
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valueChange: {
|
|
||||||
handle: async ({ form, value }) => {
|
|
||||||
const config = await pluginStore.getPluginConfig({
|
|
||||||
name: value,
|
|
||||||
type: "builtIn",
|
|
||||||
});
|
|
||||||
if (config.sysSetting?.input) {
|
|
||||||
merge(form, config.sysSetting.input);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...inputs,
|
|
||||||
triggerCron: {
|
|
||||||
title: "定时触发",
|
|
||||||
type: "text",
|
|
||||||
form: {
|
|
||||||
value: `0 ${randomMin} ${randomHour} * * *`,
|
|
||||||
component: {
|
|
||||||
name: "cron-editor",
|
|
||||||
vModel: "modelValue",
|
|
||||||
placeholder: "0 0 4 * * *",
|
|
||||||
},
|
|
||||||
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
|
|
||||||
order: 100,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notification: {
|
|
||||||
title: "失败通知",
|
|
||||||
type: "text",
|
|
||||||
form: {
|
|
||||||
value: 0,
|
|
||||||
component: {
|
|
||||||
name: NotificationSelector,
|
|
||||||
vModel: "modelValue",
|
|
||||||
on: {
|
|
||||||
selectedChange({ $event, form }) {
|
|
||||||
form.notificationTarget = $event;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
order: 101,
|
|
||||||
helper: "任务执行失败实时提醒",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ const formWrapperRef = ref();
|
||||||
const formWrapperOptions = ref();
|
const formWrapperOptions = ref();
|
||||||
const doSubmitRef = ref();
|
const doSubmitRef = ref();
|
||||||
const pluginStore = usePluginStore();
|
const pluginStore = usePluginStore();
|
||||||
async function buildFormOptions() {
|
async function buildFormOptions(req: { defaultGroupId?: number }) {
|
||||||
const pluginGroup = await pluginStore.getGroups();
|
const pluginGroup = await pluginStore.getGroups();
|
||||||
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
|
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
|
||||||
const certPluginGroup = pluginGroups.cert;
|
const certPluginGroup = pluginGroups.cert;
|
||||||
|
@ -51,10 +51,13 @@ async function buildFormOptions() {
|
||||||
}) as any
|
}) as any
|
||||||
);
|
);
|
||||||
|
|
||||||
|
formOptions.columns.groupId.value = req.defaultGroupId;
|
||||||
|
|
||||||
formWrapperOptions.value = formOptions;
|
formWrapperOptions.value = formOptions;
|
||||||
}
|
}
|
||||||
buildFormOptions();
|
|
||||||
function open(doSubmit: any) {
|
function open(doSubmit: any, req: { defaultGroupId?: number }) {
|
||||||
|
buildFormOptions(req);
|
||||||
doSubmitRef.value = doSubmit;
|
doSubmitRef.value = doSubmit;
|
||||||
formWrapperRef.value.open(formWrapperOptions.value);
|
formWrapperRef.value.open(formWrapperOptions.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
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";
|
|
||||||
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) => {
|
|
||||||
// const certDetail = readCertDetail(form.cert.crt);
|
|
||||||
// 添加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",
|
|
||||||
});
|
|
||||||
message.success("创建成功,请添加证书部署任务");
|
|
||||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
openAddCertdPipelineDialog,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
|
||||||
|
import { cloneDeep, merge, omit } from "lodash-es";
|
||||||
|
import { message } from "ant-design-vue";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { compute, CreateCrudOptionsRet, dict, useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
|
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||||
|
import { useReference } from "/@/use/use-refrence";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import * as api from "../api";
|
||||||
|
import { PluginGroup, usePluginStore } from "/@/store/plugin";
|
||||||
|
import { createNotificationApi } from "/@/views/certd/notification/api";
|
||||||
|
|
||||||
|
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 useCertPipelineCreator() {
|
||||||
|
const { openCrudFormDialog } = useFormWrapper();
|
||||||
|
|
||||||
|
const pluginStore = usePluginStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet {
|
||||||
|
const inputs: any = {};
|
||||||
|
const moreParams = [];
|
||||||
|
for (const plugin of certPlugins) {
|
||||||
|
for (const inputKey in plugin.input) {
|
||||||
|
if (inputs[inputKey]) {
|
||||||
|
//如果两个插件有的字段,直接显示
|
||||||
|
inputs[inputKey].form.show = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const inputDefine = cloneDeep(plugin.input[inputKey]);
|
||||||
|
if (!inputDefine.required && !inputDefine.maybeNeed) {
|
||||||
|
moreParams.push(inputKey);
|
||||||
|
// continue;
|
||||||
|
}
|
||||||
|
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 form?.certApplyPlugin === plugin.name && inputDefineShow;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pluginStore = usePluginStore();
|
||||||
|
const randomHour = Math.floor(Math.random() * 6);
|
||||||
|
const randomMin = Math.floor(Math.random() * 60);
|
||||||
|
|
||||||
|
const groupDictRef = dict({
|
||||||
|
url: "/pi/pipeline/group/all",
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
form: {
|
||||||
|
doSubmit,
|
||||||
|
wrapper: {
|
||||||
|
width: 1350,
|
||||||
|
saveRemind: false,
|
||||||
|
title: "创建证书流水线",
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
groups: {
|
||||||
|
more: {
|
||||||
|
header: "更多参数",
|
||||||
|
columns: moreParams,
|
||||||
|
collapsed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
certApplyPlugin: {
|
||||||
|
title: "证书申请插件",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ value: "CertApply", label: "JS-ACME" },
|
||||||
|
{ value: "CertApplyLego", label: "Lego-ACME" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
order: 0,
|
||||||
|
value: "CertApply",
|
||||||
|
helper: {
|
||||||
|
render: () => {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
<li>JS-ACME:使用简单方便,功能强大【推荐】</li>
|
||||||
|
<li>Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
valueChange: {
|
||||||
|
handle: async ({ form, value }) => {
|
||||||
|
const config = await pluginStore.getPluginConfig({
|
||||||
|
name: value,
|
||||||
|
type: "builtIn",
|
||||||
|
});
|
||||||
|
if (config.sysSetting?.input) {
|
||||||
|
merge(form, config.sysSetting.input);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...inputs,
|
||||||
|
triggerCron: {
|
||||||
|
title: "定时触发",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
value: `0 ${randomMin} ${randomHour} * * *`,
|
||||||
|
component: {
|
||||||
|
name: "cron-editor",
|
||||||
|
vModel: "modelValue",
|
||||||
|
placeholder: "0 0 4 * * *",
|
||||||
|
},
|
||||||
|
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
|
||||||
|
order: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notification: {
|
||||||
|
title: "失败通知",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
value: 0,
|
||||||
|
component: {
|
||||||
|
name: NotificationSelector,
|
||||||
|
vModel: "modelValue",
|
||||||
|
on: {
|
||||||
|
selectedChange({ $event, form }) {
|
||||||
|
form.notificationTarget = $event;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: 101,
|
||||||
|
helper: "任务执行失败实时提醒",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
groupId: {
|
||||||
|
title: "流水线分组",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: groupDictRef,
|
||||||
|
form: {
|
||||||
|
order: 9999,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCertPlugins() {
|
||||||
|
const pluginGroup = await pluginStore.getGroups();
|
||||||
|
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
|
||||||
|
const certPluginGroup = pluginGroups.cert;
|
||||||
|
|
||||||
|
const certPlugins = [];
|
||||||
|
for (const plugin of certPluginGroup.plugins) {
|
||||||
|
const detail: any = await pluginStore.getPluginDefine(plugin.name);
|
||||||
|
certPlugins.push(detail);
|
||||||
|
}
|
||||||
|
return certPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) {
|
||||||
|
//检查是否流水线数量超出限制
|
||||||
|
await checkPipelineLimit();
|
||||||
|
|
||||||
|
const wrapperRef = ref();
|
||||||
|
function getFormData() {
|
||||||
|
if (!wrapperRef.value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return wrapperRef.value.getFormData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doSubmit({ form }: any) {
|
||||||
|
// const certDetail = readCertDetail(form.cert.crt);
|
||||||
|
// 添加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", "groupId"]);
|
||||||
|
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 groupId = form.groupId;
|
||||||
|
const id = await api.Save({
|
||||||
|
title: pipeline.title,
|
||||||
|
content: JSON.stringify(pipeline),
|
||||||
|
keepHistoryCount: 30,
|
||||||
|
type: "cert",
|
||||||
|
groupId,
|
||||||
|
});
|
||||||
|
if (form.email) {
|
||||||
|
try {
|
||||||
|
//创建一个默认的邮件通知
|
||||||
|
const notificationApi = createNotificationApi();
|
||||||
|
await notificationApi.GetOrCreateDefault({ email: form.email });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message.success("创建成功,请添加证书部署任务");
|
||||||
|
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||||
|
}
|
||||||
|
const certPlugins = await getCertPlugins();
|
||||||
|
const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit);
|
||||||
|
//@ts-ignore
|
||||||
|
crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined;
|
||||||
|
const wrapper = await openCrudFormDialog({ crudOptions });
|
||||||
|
wrapperRef.value = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
openAddCertdPipelineDialog,
|
||||||
|
};
|
||||||
|
}
|
|
@ -13,15 +13,15 @@ import { cloneDeep } from "lodash-es";
|
||||||
import { useModal } from "/@/use/use-modal";
|
import { useModal } from "/@/use/use-modal";
|
||||||
import CertView from "./cert-view.vue";
|
import CertView from "./cert-view.vue";
|
||||||
import { eachStages } from "./utils";
|
import { eachStages } from "./utils";
|
||||||
import { setRunnableIds, useCertd } from "/@/views/certd/pipeline/certd-form/use";
|
import { setRunnableIds, useCertPipelineCreator } from "/@/views/certd/pipeline/certd-form/use";
|
||||||
import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use";
|
import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use";
|
||||||
|
|
||||||
export default function ({ crudExpose, context: { certdFormRef, groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const lastResRef = ref();
|
const lastResRef = ref();
|
||||||
|
|
||||||
const { openAddCertdPipelineDialog } = useCertd(certdFormRef);
|
const { openAddCertdPipelineDialog } = useCertPipelineCreator();
|
||||||
const { openUploadCreateDialog } = useCertUpload();
|
const { openUploadCreateDialog } = useCertUpload();
|
||||||
|
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
@ -157,7 +157,9 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
||||||
type: "primary",
|
type: "primary",
|
||||||
icon: "ion:ios-add-circle-outline",
|
icon: "ion:ios-add-circle-outline",
|
||||||
click() {
|
click() {
|
||||||
openAddCertdPipelineDialog();
|
const searchForm = crudExpose.getSearchValidatedFormData();
|
||||||
|
const defaultGroupId = searchForm.groupId;
|
||||||
|
openAddCertdPipelineDialog({ defaultGroupId });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
uploadCert: {
|
uploadCert: {
|
||||||
|
@ -179,7 +181,8 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
|
||||||
},
|
},
|
||||||
icon: "ion:cloud-upload-outline",
|
icon: "ion:cloud-upload-outline",
|
||||||
click() {
|
click() {
|
||||||
openUploadCreateDialog();
|
const searchForm = crudExpose.getSearchValidatedFormData();
|
||||||
|
openUploadCreateDialog({ defaultGroupId: searchForm.groupId });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
<template #form-bottom>
|
<template #form-bottom>
|
||||||
<div>申请证书</div>
|
<div>申请证书</div>
|
||||||
</template>
|
</template>
|
||||||
<pi-certd-form ref="certdFormRef"></pi-certd-form>
|
|
||||||
</fs-crud>
|
</fs-crud>
|
||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
@ -33,7 +32,6 @@ defineOptions({
|
||||||
name: "PipelineManager",
|
name: "PipelineManager",
|
||||||
});
|
});
|
||||||
|
|
||||||
const certdFormRef = ref();
|
|
||||||
const groupDictRef = dict({
|
const groupDictRef = dict({
|
||||||
url: "/pi/pipeline/group/all",
|
url: "/pi/pipeline/group/all",
|
||||||
value: "id",
|
value: "id",
|
||||||
|
@ -41,7 +39,6 @@ const groupDictRef = dict({
|
||||||
});
|
});
|
||||||
const selectedRowKeys = ref([]);
|
const selectedRowKeys = ref([]);
|
||||||
const context: any = {
|
const context: any = {
|
||||||
certdFormRef,
|
|
||||||
groupDictRef,
|
groupDictRef,
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { ReadCertDetail } from "./api";
|
import { ReadCertDetail } from "./api";
|
||||||
|
import { util } from "/@/utils";
|
||||||
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
|
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
|
||||||
if (!list || list.length <= 0) {
|
if (!list || list.length <= 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,7 +71,13 @@ export async function checkPipelineLimit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readCertDetail(crt: string) {
|
export async function readCertDetail(crt: string) {
|
||||||
return await ReadCertDetail(crt);
|
const cached = await util.cache.get(crt);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
const res = await ReadCertDetail(crt);
|
||||||
|
await util.cache.set(crt, res);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllDomainsFromCrt(crt: string) {
|
export async function getAllDomainsFromCrt(crt: string) {
|
||||||
|
|
Loading…
Reference in New Issue