mirror of https://github.com/certd/certd
fix: 修复流水线页面状态没有刷新的bug
parent
95332d5db9
commit
93e9498b41
|
@ -16,6 +16,10 @@ export default class SshOssClientImpl extends BaseOssClient<SshAccess> {
|
|||
throw new Error("Method not implemented.");
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
if (!filePath) {
|
||||
filePath = "";
|
||||
}
|
||||
filePath = filePath.trim();
|
||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
||||
|
||||
// Write file to temp path
|
||||
|
|
|
@ -374,6 +374,7 @@ export default defineComponent({
|
|||
const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id });
|
||||
currentHistory.value.logs = detail.logs;
|
||||
currentHistory.value.pipeline = detail.pipeline;
|
||||
currentHistory.value.status = detail.pipeline.status.result;
|
||||
};
|
||||
const changeCurrentHistory = async (history?: RunHistory) => {
|
||||
if (!history) {
|
||||
|
@ -385,7 +386,7 @@ export default defineComponent({
|
|||
currentHistory.value = history;
|
||||
await loadCurrentHistoryDetail();
|
||||
pipeline.value = currentHistory.value.pipeline;
|
||||
currentPipeline.value = cloneDeep(pipeline.value);
|
||||
currentPipeline.value = currentHistory.value.pipeline;
|
||||
};
|
||||
|
||||
async function loadHistoryList(reload = false) {
|
||||
|
@ -441,6 +442,11 @@ export default defineComponent({
|
|||
if (currentHistory.value != null) {
|
||||
if (currentHistory.value.pipeline?.status?.status === "start") {
|
||||
await loadCurrentHistoryDetail();
|
||||
pipeline.value = currentHistory.value.pipeline;
|
||||
// if (currentHistory.value.pipeline?.status?.status !== "start") {
|
||||
// 不传true好像不会刷新
|
||||
// await loadHistoryList(true);
|
||||
// }
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from './host-shell-execute/index.js';
|
||||
export * from './upload-to-host/index.js';
|
||||
export * from './copy-to-local/index.js'
|
||||
export * from './plugin-upload-to-oss.js'
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
import {AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
|
||||
import {CertInfo} from "@certd/plugin-cert";
|
||||
import {ossClientFactory} from "@certd/plugin-lib";
|
||||
import {utils} from "@certd/basic";
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: 'UploadCertToOss',
|
||||
title: '上传证书到对象存储OSS',
|
||||
icon: 'ri:rest-time-line',
|
||||
desc: '支持阿里云OSS、腾讯云COS、七牛云KODO、S3、MinIO、FTP、SFTP',
|
||||
group: pluginGroups.other.key,
|
||||
showRunStrategy:false,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
export class UploadCertToOssPlugin extends AbstractTaskPlugin {
|
||||
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
helper: '请选择前置任务输出的域名证书',
|
||||
component: {
|
||||
name: 'output-selector',
|
||||
from: [":cert:"],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: 'OSS类型',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel:"value",
|
||||
options: [
|
||||
{ label: "阿里云OSS", value: "alioss" },
|
||||
{ label: "腾讯云COS", value: "tencentcos" },
|
||||
{ label: "七牛OSS", value: "qiniuoss" },
|
||||
{ label: "S3/Minio", value: "s3" },
|
||||
{ label: "SFTP", value: "sftp" },
|
||||
{ label: "FTP", value: "ftp" },
|
||||
]
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
uploaderType!: string;
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: 'OSS授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
},
|
||||
required: true,
|
||||
mergeScript: `
|
||||
return {
|
||||
component: {
|
||||
type: ctx.compute(({form})=>{
|
||||
return form.uploaderType;
|
||||
})
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: '证书格式',
|
||||
helper: '要部署的证书格式,支持pem、pfx、der、jks',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
options: [
|
||||
{ value: 'pem', label: 'pem(crt),Nginx等大部分应用' },
|
||||
{ value: 'pfx', label: 'pfx,一般用于IIS' },
|
||||
{ value: 'der', label: 'der,一般用于Apache' },
|
||||
{ value: 'jks', label: 'jks,一般用于JAVA应用' },
|
||||
{ value: 'one', label: '证书私钥一体,crt+key简单合并为一个pem文件' },
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
certType!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '证书保存路径',
|
||||
helper: '路径要包含证书文件名,例如:/tmp/cert.pem',
|
||||
component: {
|
||||
placeholder: '/root/deploy/nginx/full_chain.pem',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'pem';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
crtPath!: string;
|
||||
@TaskInput({
|
||||
title: '私钥保存路径',
|
||||
helper: '原本的私钥保存路径,需要有写入权限,路径要包含私钥文件名,例如:/tmp/cert.key',
|
||||
component: {
|
||||
placeholder: '/root/deploy/nginx/cert.key',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'pem';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
keyPath!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '中间证书保存路径',
|
||||
helper: '路径要包含文件名,一般情况传上面两个文件即可,极少数情况需要这个中间证书',
|
||||
component: {
|
||||
placeholder: '/root/deploy/nginx/intermediate.pem',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'pem';
|
||||
})
|
||||
}
|
||||
`,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
icPath!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: 'PFX证书保存路径',
|
||||
helper: '路径要包含证书文件名,例如:D:\\iis\\cert.pfx',
|
||||
component: {
|
||||
placeholder: 'D:\\iis\\cert.pfx',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'pfx';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
pfxPath!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: 'DER证书保存路径',
|
||||
helper: '路径要包含证书文件名,例如:/tmp/cert.der',
|
||||
component: {
|
||||
placeholder: '/root/deploy/apache/cert.der',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'der';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
derPath!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: 'jks证书保存路径',
|
||||
helper: '路径要包含证书文件名,例如:/tmp/cert.jks',
|
||||
component: {
|
||||
placeholder: '/root/deploy/java_app/cert.jks',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'jks';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
jksPath!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '一体证书保存路径',
|
||||
helper: '路径要包含证书文件名,例如:/tmp/crt_key.pem',
|
||||
component: {
|
||||
placeholder: '/app/crt_key.pem',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.certType === 'one';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
rules: [{ type: 'filepath' }],
|
||||
})
|
||||
onePath!: string;
|
||||
|
||||
|
||||
async onInstance() {}
|
||||
async execute(): Promise<void> {
|
||||
const { accessId } = this;
|
||||
let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath } = this;
|
||||
if (!accessId) {
|
||||
throw new Error('OSS授权配置不能为空');
|
||||
}
|
||||
|
||||
const uploaderType = this.uploaderType
|
||||
const uploaderAccess = this.accessId
|
||||
|
||||
const httpUploaderContext = {
|
||||
accessService: this.ctx.accessService,
|
||||
logger: this.logger,
|
||||
utils,
|
||||
};
|
||||
|
||||
const access = await this.getAccess(uploaderAccess);
|
||||
this.logger.info("上传方式", uploaderType);
|
||||
const httpUploader = await ossClientFactory.createOssClientByType(uploaderType, {
|
||||
access,
|
||||
rootDir: "",
|
||||
ctx: httpUploaderContext,
|
||||
});
|
||||
|
||||
|
||||
this.logger.info('准备上传文件到OSS');
|
||||
|
||||
if (crtPath) {
|
||||
await httpUploader.upload(crtPath, Buffer.from(this.cert.crt))
|
||||
this.logger.info(`上传证书:${crtPath}`);
|
||||
}
|
||||
if (keyPath) {
|
||||
await httpUploader.upload(keyPath, Buffer.from(this.cert.key))
|
||||
this.logger.info(`上传私钥:${keyPath}`);
|
||||
}
|
||||
if (icPath) {
|
||||
await httpUploader.upload(icPath, Buffer.from(this.cert.ic))
|
||||
this.logger.info(`上传中间证书:${icPath}`);
|
||||
}
|
||||
if (pfxPath) {
|
||||
await httpUploader.upload(pfxPath, Buffer.from(this.cert.pfx, "base64"))
|
||||
this.logger.info(`上传PFX证书:${pfxPath}`);
|
||||
}
|
||||
if (derPath) {
|
||||
await httpUploader.upload(derPath, Buffer.from(this.cert.der, "base64"))
|
||||
this.logger.info(`上传DER证书:${derPath}`);
|
||||
}
|
||||
if (this.jksPath) {
|
||||
await httpUploader.upload(jksPath,Buffer.from(this.cert.jks, "base64"))
|
||||
this.logger.info(`上传jks证书:${jksPath}`);
|
||||
}
|
||||
|
||||
if (onePath) {
|
||||
await httpUploader.upload(onePath, Buffer.from(this.cert.one))
|
||||
this.logger.info(`上传一体证书:${onePath}`);
|
||||
}
|
||||
|
||||
this.logger.info('上传文件成功');
|
||||
}
|
||||
}
|
||||
new UploadCertToOssPlugin();
|
|
@ -50,6 +50,60 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
|
|||
})
|
||||
email!: string[];
|
||||
|
||||
|
||||
/**
|
||||
* title:
|
||||
* title: 邮件标题
|
||||
* helper: |-
|
||||
* 请输入邮件标题否则将使用默认标题
|
||||
* 域名:${certDomains}
|
||||
* component:
|
||||
* name: a-input
|
||||
* required: false
|
||||
* template:
|
||||
* title: 邮件模版
|
||||
* helper: |-
|
||||
* 请输入模版内容否则将使用默认模版
|
||||
* 域名:${certDomains}
|
||||
* value: |-
|
||||
* 尊敬的用户你好:
|
||||
* 以下是域名(${certDomains})证书文件
|
||||
* component:
|
||||
* name: a-textarea
|
||||
* autosize:
|
||||
* minRows: 6
|
||||
* maxRows: 10
|
||||
* required: false
|
||||
*/
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: '邮件标题',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
vModel: 'value',
|
||||
},
|
||||
helper: '请输入邮件标题否则将使用默认标题\n模板变量在标题中也可以使用',
|
||||
required: false,
|
||||
})
|
||||
title!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '邮件模版',
|
||||
component: {
|
||||
name: 'a-textarea',
|
||||
vModel: 'value',
|
||||
autosize: {
|
||||
minRows: 6,
|
||||
maxRows: 10,
|
||||
},
|
||||
},
|
||||
helper: `请输入模版内容否则将使用默认模版
|
||||
变量:主域名=$\{mainDomain}、全部域名=$\{domains}、过期时间=$\{expiresTime}、备注=$\{remark}`,
|
||||
required: false,
|
||||
})
|
||||
template!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '备注',
|
||||
component: {
|
||||
|
@ -67,15 +121,33 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
|
|||
const certReader = new CertReader(this.cert)
|
||||
const mainDomain = certReader.getMainDomain();
|
||||
const domains = certReader.getAllDomains().join(',');
|
||||
const title = `证书申请成功【${mainDomain}】`;
|
||||
const html = `
|
||||
|
||||
|
||||
const data = {
|
||||
mainDomain,
|
||||
domains,
|
||||
expiresTime: dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss"),
|
||||
remark:this.remark
|
||||
}
|
||||
|
||||
let title = `证书申请成功【${mainDomain}】`;
|
||||
let html = `
|
||||
<div>
|
||||
<p>证书申请成功</p>
|
||||
<p>域名:${domains}</p>
|
||||
<p>证书有效期:${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")}</p>
|
||||
<p>证书有效期:${data.expiresTime}</p>
|
||||
<p>备注:${this.remark||""}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (this.title) {
|
||||
const compile = this.compile(this.title);
|
||||
title = compile(data);
|
||||
}
|
||||
if (this.template) {
|
||||
const compile = this.compile(this.template);
|
||||
html = compile(data);
|
||||
}
|
||||
const file = this.certZip
|
||||
if (!file) {
|
||||
throw new Error('证书压缩文件还未生成,重新运行证书任务');
|
||||
|
@ -91,8 +163,13 @@ export class DeployCertToMailPlugin extends AbstractTaskPlugin {
|
|||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
compile(templateString:string) {
|
||||
return new Function('data', ` with(data || {}) {
|
||||
return \`${templateString}\`;
|
||||
}
|
||||
`);
|
||||
}
|
||||
}
|
||||
new DeployCertToMailPlugin();
|
||||
|
|
Loading…
Reference in New Issue