mirror of https://github.com/certd/certd
perf: 上传到主机插件支持复制到本机路径
parent
d9eb927b0a
commit
92446c3399
|
@ -1,4 +1,15 @@
|
||||||
import { AbstractTaskPlugin, Decorator, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline";
|
import {
|
||||||
|
AbstractTaskPlugin,
|
||||||
|
Decorator,
|
||||||
|
HttpClient,
|
||||||
|
IAccessService,
|
||||||
|
IContext,
|
||||||
|
IsTaskPlugin,
|
||||||
|
RunStrategy,
|
||||||
|
Step,
|
||||||
|
TaskInput,
|
||||||
|
TaskOutput
|
||||||
|
} from "@certd/pipeline";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {AcmeService, CertInfo} from "./acme";
|
import {AcmeService, CertInfo} from "./acme";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
@ -6,7 +17,7 @@ import { Logger } from "log4js";
|
||||||
import {DnsProviderDefine, dnsProviderRegistry} from "../../dns-provider";
|
import {DnsProviderDefine, dnsProviderRegistry} from "../../dns-provider";
|
||||||
import {CertReader} from "./cert-reader";
|
import {CertReader} from "./cert-reader";
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
import { fileUtils } from "@certd/pipeline";
|
|
||||||
export { CertReader };
|
export { CertReader };
|
||||||
export type { CertInfo };
|
export type { CertInfo };
|
||||||
|
|
||||||
|
@ -150,7 +161,7 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||||
const cert: CertInfo = certReader.toCertInfo();
|
const cert: CertInfo = certReader.toCertInfo();
|
||||||
this.cert = cert;
|
this.cert = cert;
|
||||||
// this.logger.info(JSON.stringify(certReader.detail));
|
// this.logger.info(JSON.stringify(certReader.detail));
|
||||||
const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDDHHmmss");
|
const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDD_HHmmss");
|
||||||
await this.zipCert(cert, applyTime);
|
await this.zipCert(cert, applyTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,8 +173,7 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||||
const filename = `cert_${domain_name}_${applyTime}.zip`;
|
const filename = `cert_${domain_name}_${applyTime}.zip`;
|
||||||
const content = await zip.generateAsync({ type: "nodebuffer" });
|
const content = await zip.generateAsync({ type: "nodebuffer" });
|
||||||
this.saveFile(filename, content);
|
this.saveFile(filename, content);
|
||||||
this.logger.info(`getFileRootDir:${fileUtils.getFileRootDir("11")}`);
|
this.logger.info(`已保存文件:${filename}`);
|
||||||
this.logger.info(`保存文件:${filename}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,3 +10,23 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cd-icon-button{
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-flex{
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-flex-inline{
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: left;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,10 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
margin-left:5px;
|
margin-left:5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mr-5{
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.mt-10{
|
.mt-10{
|
||||||
margin-top:10px;
|
margin-top:10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
width: "150px"
|
width: "180px"
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
rowSelection: {
|
rowSelection: {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="pi-access-selector">
|
<div class="pi-access-selector">
|
||||||
<span v-if="target.name" class="mlr-5">{{ target.name }}</span>
|
<span v-if="target.name" class="mr-5 cd-flex-inline">
|
||||||
|
<span class="mr-5">{{ target.name }}</span>
|
||||||
|
<fs-icon class="cd-icon-button" icon="ion:close-circle-outline" @click="clear"></fs-icon>
|
||||||
|
</span>
|
||||||
<span v-else class="mlr-5 gray">请选择</span>
|
<span v-else class="mlr-5 gray">请选择</span>
|
||||||
<a-button @click="chooseForm.open">选择</a-button>
|
<a-button @click="chooseForm.open">选择</a-button>
|
||||||
<a-form-item-rest v-if="chooseForm.show">
|
<a-form-item-rest v-if="chooseForm.show">
|
||||||
|
@ -42,6 +45,13 @@ export default defineComponent({
|
||||||
target.value = await api.GetObj(value);
|
target.value = await api.GetObj(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clear(){
|
||||||
|
selectedId.value = "";
|
||||||
|
target.value = null
|
||||||
|
ctx.emit("update:modelValue", selectedId.value);
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => {
|
() => {
|
||||||
return props.modelValue;
|
return props.modelValue;
|
||||||
|
@ -89,7 +99,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
clear,
|
||||||
target,
|
target,
|
||||||
selectedId,
|
selectedId,
|
||||||
providerDefine,
|
providerDefine,
|
||||||
|
|
|
@ -23,6 +23,9 @@ export class AccessService
|
||||||
|
|
||||||
async getById(id: any): Promise<any> {
|
async getById(id: any): Promise<any> {
|
||||||
const entity = await this.info(id);
|
const entity = await this.info(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
|
||||||
|
}
|
||||||
// const access = accessRegistry.get(entity.type);
|
// const access = accessRegistry.get(entity.type);
|
||||||
const setting = JSON.parse(entity.setting);
|
const setting = JSON.parse(entity.setting);
|
||||||
return {
|
return {
|
||||||
|
@ -38,5 +41,4 @@ export class AccessService
|
||||||
getDefineByType(type: string) {
|
getDefineByType(type: string) {
|
||||||
return accessRegistry.getDefine(type);
|
return accessRegistry.getDefine(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import * as fs from 'fs';
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: 'uploadCertToHost',
|
name: 'uploadCertToHost',
|
||||||
title: '上传证书到主机',
|
title: '上传证书到主机',
|
||||||
|
desc: '也支持复制证书到本机',
|
||||||
default: {
|
default: {
|
||||||
strategy: {
|
strategy: {
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
|
@ -53,10 +54,22 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
name: 'pi-access-selector',
|
name: 'pi-access-selector',
|
||||||
type: 'ssh',
|
type: 'ssh',
|
||||||
},
|
},
|
||||||
rules: [{ required: true, message: '此项必填' }],
|
rules: [{ required: false, message: '' }],
|
||||||
})
|
})
|
||||||
accessId!: string;
|
accessId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: '复制到当前主机',
|
||||||
|
helper:
|
||||||
|
'开启后,将直接复制到当前主机某个目录,由于是docker启动,实际上复制到的是docker容器内的目录,你需要事先在docker-compose.yaml中配置主机目录映射: volumes: /your_target_path:/your_target_path',
|
||||||
|
component: {
|
||||||
|
name: 'a-switch',
|
||||||
|
value: false,
|
||||||
|
vModel: 'checked',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
copyToThisHost!: boolean;
|
||||||
|
|
||||||
@TaskOutput({
|
@TaskOutput({
|
||||||
title: '证书保存路径',
|
title: '证书保存路径',
|
||||||
})
|
})
|
||||||
|
@ -74,15 +87,31 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
this.accessService = this.ctx.accessService;
|
this.accessService = this.ctx.accessService;
|
||||||
this.logger = this.ctx.logger;
|
this.logger = this.ctx.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copyFile(srcFile: string, destFile: string) {
|
||||||
|
const dir = destFile.substring(0, destFile.lastIndexOf('/'));
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
fs.copyFileSync(srcFile, destFile);
|
||||||
|
}
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
const { crtPath, keyPath, cert, accessId } = this;
|
const { crtPath, keyPath, cert, accessId } = this;
|
||||||
const certReader = new CertReader(cert);
|
const certReader = new CertReader(cert);
|
||||||
const connectConf = await this.accessService.getById(accessId);
|
|
||||||
const sshClient = new SshClient(this.logger);
|
|
||||||
|
|
||||||
const saveCrtPath = certReader.saveToFile('crt');
|
const saveCrtPath = certReader.saveToFile('crt');
|
||||||
const saveKeyPath = certReader.saveToFile('key');
|
const saveKeyPath = certReader.saveToFile('key');
|
||||||
|
|
||||||
|
if (this.copyToThisHost) {
|
||||||
|
this.copyFile(saveCrtPath, crtPath);
|
||||||
|
this.copyFile(saveKeyPath, keyPath);
|
||||||
|
this.logger.info('证书复制成功:crtPath=', crtPath, ',keyPath=', keyPath);
|
||||||
|
} else {
|
||||||
|
if (!accessId) {
|
||||||
|
throw new Error('主机登录授权配置不能为空');
|
||||||
|
}
|
||||||
|
const connectConf = await this.accessService.getById(accessId);
|
||||||
|
const sshClient = new SshClient(this.logger);
|
||||||
await sshClient.uploadFiles({
|
await sshClient.uploadFiles({
|
||||||
connectConf,
|
connectConf,
|
||||||
transports: [
|
transports: [
|
||||||
|
@ -97,6 +126,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
this.logger.info('证书上传成功:crtPath=', crtPath, ',keyPath=', keyPath);
|
this.logger.info('证书上传成功:crtPath=', crtPath, ',keyPath=', keyPath);
|
||||||
|
}
|
||||||
|
|
||||||
//删除临时文件
|
//删除临时文件
|
||||||
fs.unlinkSync(saveCrtPath);
|
fs.unlinkSync(saveCrtPath);
|
||||||
|
|
Loading…
Reference in New Issue