mirror of https://github.com/certd/certd
chore: 模版创建流水线
parent
9296ba7492
commit
29906ec057
|
@ -12,6 +12,25 @@ import { PluginGroup, usePluginStore } from "/@/store/plugin";
|
||||||
import { createNotificationApi } from "/@/views/certd/notification/api";
|
import { createNotificationApi } from "/@/views/certd/notification/api";
|
||||||
import GroupSelector from "../group/group-selector.vue";
|
import GroupSelector from "../group/group-selector.vue";
|
||||||
|
|
||||||
|
export function fillPipelineByDefaultForm(pipeline: any, form: any) {
|
||||||
|
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 || "自定义通知",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pipeline.triggers = triggers;
|
||||||
|
pipeline.notifications = notifications;
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
export function setRunnableIds(pipeline: any) {
|
export function setRunnableIds(pipeline: any) {
|
||||||
const idMap: any = {};
|
const idMap: any = {};
|
||||||
function createId(oldId: any) {
|
function createId(oldId: any) {
|
||||||
|
@ -244,21 +263,8 @@ export function useCertPipelineCreator() {
|
||||||
async function doSubmit({ form }: any) {
|
async function doSubmit({ form }: any) {
|
||||||
// const certDetail = readCertDetail(form.cert.crt);
|
// const certDetail = readCertDetail(form.cert.crt);
|
||||||
// 添加certd pipeline
|
// 添加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"]);
|
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]);
|
||||||
let pipeline = {
|
let pipeline: any = {
|
||||||
title: form.domains[0] + "证书自动化",
|
title: form.domains[0] + "证书自动化",
|
||||||
runnableType: "pipeline",
|
runnableType: "pipeline",
|
||||||
stages: [
|
stages: [
|
||||||
|
@ -288,17 +294,11 @@ export function useCertPipelineCreator() {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
triggers,
|
|
||||||
notifications,
|
|
||||||
};
|
};
|
||||||
pipeline = setRunnableIds(pipeline);
|
|
||||||
|
|
||||||
/**
|
pipeline = fillPipelineByDefaultForm(pipeline, form);
|
||||||
* // cert: 证书; backup: 备份; custom:自定义;
|
|
||||||
* type: string;
|
pipeline = setRunnableIds(pipeline);
|
||||||
* // custom: 自定义; monitor: 监控;
|
|
||||||
* from: string;
|
|
||||||
*/
|
|
||||||
const groupId = form.groupId;
|
const groupId = form.groupId;
|
||||||
const id = await api.Save({
|
const id = await api.Save({
|
||||||
title: pipeline.title,
|
title: pipeline.title,
|
||||||
|
|
|
@ -56,4 +56,12 @@ export const templateApi = {
|
||||||
method: "post",
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async CreatePipelineByTemplate(data: any) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/createPipelineByTemplate",
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,8 +146,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
toolbar: {
|
toolbar: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
tabs: {
|
columns: {
|
||||||
name: "type",
|
title: {
|
||||||
|
column: {
|
||||||
|
cellRender: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a-form ref="templateFormRef" class="template-form w-full" :model="templateForm" :label-col="labelCol" :wrapper-col="wrapperCol">
|
<a-form ref="formRef" class="template-form w-full" :model="templateForm" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
<template v-for="(item, key) in templateFormColumns" :key="key">
|
<template v-for="(item, key) in templateFormColumns" :key="key">
|
||||||
<fs-form-item v-if="item.show !== false" :model-value="get(templateForm, key)" :item="item" :get-context-fn="getScopeFunc(key)" @update:model-value="set(templateForm, key, $event)" />
|
<fs-form-item v-if="item.show !== false" :model-value="get(templateForm, key)" :item="item" :get-context-fn="getScopeFunc(key)" @update:model-value="set(templateForm, key, $event)" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -14,7 +14,7 @@ import { usePluginStore } from "/@/store/plugin";
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "TemplateForm",
|
name: "TemplateForm",
|
||||||
});
|
});
|
||||||
|
const formRef = ref();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
input: any;
|
input: any;
|
||||||
pipeline: any;
|
pipeline: any;
|
||||||
|
@ -61,9 +61,14 @@ function getScopeFunc(inputKey: string) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function validate() {
|
||||||
|
return await formRef.value.validate();
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
getForm() {
|
getForm() {
|
||||||
return templateForm;
|
return templateForm;
|
||||||
},
|
},
|
||||||
|
validate,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { dict, useFormWrapper } from "@fast-crud/fast-crud";
|
import { dict, useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
|
import { checkPipelineLimit, eachSteps } from "/@/views/certd/pipeline/utils";
|
||||||
import { templateApi } from "/@/views/certd/pipeline/template/api";
|
import { templateApi } from "/@/views/certd/pipeline/template/api";
|
||||||
import TemplateForm from "./form.vue";
|
import TemplateForm from "./form.vue";
|
||||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||||
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
|
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { fillPipelineByDefaultForm } from "/@/views/certd/pipeline/certd-form/use";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
|
||||||
export function useTemplate() {
|
export function useTemplate() {
|
||||||
const { openCrudFormDialog } = useFormWrapper();
|
const { openCrudFormDialog } = useFormWrapper();
|
||||||
|
@ -38,8 +40,43 @@ export function useTemplate() {
|
||||||
|
|
||||||
const randomHour = Math.floor(Math.random() * 6);
|
const randomHour = Math.floor(Math.random() * 6);
|
||||||
const randomMin = Math.floor(Math.random() * 60);
|
const randomMin = Math.floor(Math.random() * 60);
|
||||||
|
const templateFormRef = ref();
|
||||||
|
|
||||||
|
async function onSubmit(opts: { form: any }) {
|
||||||
|
const form = opts.form;
|
||||||
|
await templateFormRef.value.validate();
|
||||||
|
|
||||||
|
const tempInputs = templateFormRef.value.getFormData();
|
||||||
|
|
||||||
|
let newPipeline = cloneDeep(pipeline);
|
||||||
|
newPipeline = fillPipelineByDefaultForm(newPipeline, form);
|
||||||
|
//填充模版参数
|
||||||
|
const steps: any = {};
|
||||||
|
eachSteps(newPipeline, (step: any) => {
|
||||||
|
steps[step.id] = step;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const inputKey in tempInputs) {
|
||||||
|
const [stepId, key] = inputKey.split(".");
|
||||||
|
const step = steps[stepId];
|
||||||
|
if (step) {
|
||||||
|
step.input[key] = tempInputs[inputKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupId = form.groupId;
|
||||||
|
const { id } = await templateApi.CreatePipelineByTemplate({
|
||||||
|
title: form.title,
|
||||||
|
content: JSON.stringify(newPipeline),
|
||||||
|
keepHistoryCount: 30,
|
||||||
|
groupId,
|
||||||
|
templateId: detail.template.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const crudOptions = {
|
const crudOptions = {
|
||||||
form: {
|
form: {
|
||||||
|
onSubmit,
|
||||||
wrapper: {
|
wrapper: {
|
||||||
title: `从模版<${detail.template.title}>创建流水线`,
|
title: `从模版<${detail.template.title}>创建流水线`,
|
||||||
width: 1100,
|
width: 1100,
|
||||||
|
@ -47,7 +84,7 @@ export function useTemplate() {
|
||||||
"form-body-top": () => {
|
"form-body-top": () => {
|
||||||
return (
|
return (
|
||||||
<div class={"w-full flex"}>
|
<div class={"w-full flex"}>
|
||||||
<TemplateForm input={templateProps.input} pipeline={pipeline} />
|
<TemplateForm ref={templateFormRef} input={templateProps.input} pipeline={pipeline} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,6 +6,7 @@ CREATE TABLE "pi_template"
|
||||||
"title" varchar(1024),
|
"title" varchar(1024),
|
||||||
"content" text,
|
"content" text,
|
||||||
"order" integer,
|
"order" integer,
|
||||||
|
"desc" varchar(1024),
|
||||||
"disabled" boolean NOT NULL DEFAULT (false),
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
@ -14,5 +15,6 @@ CREATE TABLE "pi_template"
|
||||||
CREATE INDEX "index_template_user_id" ON "pi_template" ("user_id");
|
CREATE INDEX "index_template_user_id" ON "pi_template" ("user_id");
|
||||||
CREATE INDEX "index_template_pipeline_id" ON "pi_template" ("pipeline_id");
|
CREATE INDEX "index_template_pipeline_id" ON "pi_template" ("pipeline_id");
|
||||||
|
|
||||||
ALTER TABLE pi_pipeline ADD COLUMN "template_id" integer DEFAULT 0;
|
ALTER TABLE pi_pipeline ADD COLUMN "template_id" integer DEFAULT (0);
|
||||||
|
ALTER TABLE pi_pipeline ADD COLUMN "is_template" boolean DEFAULT (0);
|
||||||
CREATE INDEX "index_pipeline_template_id" ON "pi_pipeline" ("template_id");
|
CREATE INDEX "index_pipeline_template_id" ON "pi_pipeline" ("template_id");
|
||||||
|
|
|
@ -74,4 +74,10 @@ export class TemplateController extends CrudController<TemplateService> {
|
||||||
const detail = await this.service.detail(id, this.getUserId());
|
const detail = await this.service.detail(id, this.getUserId());
|
||||||
return this.ok(detail);
|
return this.ok(detail);
|
||||||
}
|
}
|
||||||
|
@Post('/createPipelineByTemplate', { summary: Constants.per.authOnly })
|
||||||
|
async createPipelineByTemplate(@Body(ALL) body: any) {
|
||||||
|
body.userId = this.getUserId();
|
||||||
|
const res = await this.service.createPipelineByTemplate(body);
|
||||||
|
return this.ok(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,12 @@ export class PipelineEntity {
|
||||||
@Column({ comment: '来源', nullable: true, default: '' })
|
@Column({ comment: '来源', nullable: true, default: '' })
|
||||||
from: string;
|
from: string;
|
||||||
|
|
||||||
@Column({ name:"template_id", comment: '是否模版', nullable: true, default: '' })
|
@Column({ name:"template_id", comment: '关联模版id', nullable: true, default: 0 })
|
||||||
templateId: number;
|
templateId: number;
|
||||||
|
|
||||||
|
@Column({ name:"is_template", comment: '是否模版', nullable: true, default: false })
|
||||||
|
isTemplate: boolean;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
name: 'last_history_time',
|
name: 'last_history_time',
|
||||||
comment: '最后一次执行时间',
|
comment: '最后一次执行时间',
|
||||||
|
|
|
@ -23,6 +23,8 @@ export class TemplateEntity {
|
||||||
|
|
||||||
@Column({ name: 'title', comment: '标题' })
|
@Column({ name: 'title', comment: '标题' })
|
||||||
title: string;
|
title: string;
|
||||||
|
@Column({ name: 'desc', comment: '说明' })
|
||||||
|
desc: string;
|
||||||
|
|
||||||
@Column({ comment: '配置', length: 40960 })
|
@Column({ comment: '配置', length: 40960 })
|
||||||
content: string;
|
content: string;
|
||||||
|
|
|
@ -119,7 +119,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||||
|
|
||||||
async page(pageReq: PageReq<PipelineEntity>) {
|
async page(pageReq: PageReq<PipelineEntity>) {
|
||||||
//模版流水线不要被查询出来
|
//模版流水线不要被查询出来
|
||||||
set(pageReq,"query.templateId",0)
|
set(pageReq,"query.isTemplate",false)
|
||||||
const result = await super.page(pageReq);
|
const result = await super.page(pageReq);
|
||||||
await this.fillLastVars(result.records);
|
await this.fillLastVars(result.records);
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ export class TemplateService extends BaseService<TemplateEntity> {
|
||||||
newPipeline.id = undefined;
|
newPipeline.id = undefined;
|
||||||
newPipeline.title = template.title + "模版流水线"
|
newPipeline.title = template.title + "模版流水线"
|
||||||
newPipeline.templateId = template.id
|
newPipeline.templateId = template.id
|
||||||
|
newPipeline.isTemplate = true
|
||||||
|
|
||||||
const pipelineJson: Pipeline = JSON.parse(newPipeline.content)
|
const pipelineJson: Pipeline = JSON.parse(newPipeline.content)
|
||||||
delete pipelineJson.triggers
|
delete pipelineJson.triggers
|
||||||
|
@ -86,6 +87,7 @@ export class TemplateService extends BaseService<TemplateEntity> {
|
||||||
pipeline,
|
pipeline,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async batchDelete(ids: number[], userId: number) {
|
async batchDelete(ids: number[], userId: number) {
|
||||||
|
|
||||||
const where: any = {
|
const where: any = {
|
||||||
|
@ -100,5 +102,25 @@ export class TemplateService extends BaseService<TemplateEntity> {
|
||||||
await this.delete(ids);
|
await this.delete(ids);
|
||||||
await this.pipelineService.batchDelete(pipelineIds, userId)
|
await this.pipelineService.batchDelete(pipelineIds, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createPipelineByTemplate(body: PipelineEntity) {
|
||||||
|
const templateId = body.templateId;
|
||||||
|
const template = await this.info(templateId);
|
||||||
|
|
||||||
|
if (!template && template.userId !== body.userId) {
|
||||||
|
throw new Error('模板不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempPipeline = await this.pipelineService.info(template.pipelineId)
|
||||||
|
|
||||||
|
const newPipeline = {
|
||||||
|
type: tempPipeline.type,
|
||||||
|
from : "template",
|
||||||
|
keepHistoryCount: tempPipeline.keepHistoryCount,
|
||||||
|
... body,
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.pipelineService.save(newPipeline)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue