mirror of https://github.com/certd/certd
perf: 上传到主机插件支持复制到本机路径
parent
d9eb927b0a
commit
92446c3399
|
@ -1,12 +1,23 @@
|
|||
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 { AcmeService, CertInfo } from "./acme";
|
||||
import {AcmeService, CertInfo} from "./acme";
|
||||
import _ from "lodash";
|
||||
import { Logger } from "log4js";
|
||||
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
|
||||
import { CertReader } from "./cert-reader";
|
||||
import {Logger} from "log4js";
|
||||
import {DnsProviderDefine, dnsProviderRegistry} from "../../dns-provider";
|
||||
import {CertReader} from "./cert-reader";
|
||||
import JSZip from "jszip";
|
||||
import { fileUtils } from "@certd/pipeline";
|
||||
|
||||
export { CertReader };
|
||||
export type { CertInfo };
|
||||
|
||||
|
@ -150,7 +161,7 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
|||
const cert: CertInfo = certReader.toCertInfo();
|
||||
this.cert = cert;
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -162,8 +173,7 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
|||
const filename = `cert_${domain_name}_${applyTime}.zip`;
|
||||
const content = await zip.generateAsync({ type: "nodebuffer" });
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
.mr-5{
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mt-10{
|
||||
margin-top:10px;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
},
|
||||
rowHandle: {
|
||||
width: "150px"
|
||||
width: "180px"
|
||||
},
|
||||
table: {
|
||||
rowSelection: {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<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>
|
||||
<a-button @click="chooseForm.open">选择</a-button>
|
||||
<a-form-item-rest v-if="chooseForm.show">
|
||||
|
@ -42,6 +45,13 @@ export default defineComponent({
|
|||
target.value = await api.GetObj(value);
|
||||
}
|
||||
}
|
||||
|
||||
function clear(){
|
||||
selectedId.value = "";
|
||||
target.value = null
|
||||
ctx.emit("update:modelValue", selectedId.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue;
|
||||
|
@ -89,7 +99,9 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
clear,
|
||||
target,
|
||||
selectedId,
|
||||
providerDefine,
|
||||
|
|
|
@ -23,6 +23,9 @@ export class AccessService
|
|||
|
||||
async getById(id: any): Promise<any> {
|
||||
const entity = await this.info(id);
|
||||
if (entity == null) {
|
||||
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
|
||||
}
|
||||
// const access = accessRegistry.get(entity.type);
|
||||
const setting = JSON.parse(entity.setting);
|
||||
return {
|
||||
|
@ -38,5 +41,4 @@ export class AccessService
|
|||
getDefineByType(type: string) {
|
||||
return accessRegistry.getDefine(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import * as fs from 'fs';
|
|||
@IsTaskPlugin({
|
||||
name: 'uploadCertToHost',
|
||||
title: '上传证书到主机',
|
||||
desc: '也支持复制证书到本机',
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
|
@ -53,10 +54,22 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
|||
name: 'pi-access-selector',
|
||||
type: 'ssh',
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }],
|
||||
rules: [{ required: false, message: '' }],
|
||||
})
|
||||
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({
|
||||
title: '证书保存路径',
|
||||
})
|
||||
|
@ -74,29 +87,46 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
|||
this.accessService = this.ctx.accessService;
|
||||
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> {
|
||||
const { crtPath, keyPath, cert, accessId } = this;
|
||||
const certReader = new CertReader(cert);
|
||||
const connectConf = await this.accessService.getById(accessId);
|
||||
const sshClient = new SshClient(this.logger);
|
||||
|
||||
const saveCrtPath = certReader.saveToFile('crt');
|
||||
const saveKeyPath = certReader.saveToFile('key');
|
||||
|
||||
await sshClient.uploadFiles({
|
||||
connectConf,
|
||||
transports: [
|
||||
{
|
||||
localPath: saveCrtPath,
|
||||
remotePath: crtPath,
|
||||
},
|
||||
{
|
||||
localPath: saveKeyPath,
|
||||
remotePath: keyPath,
|
||||
},
|
||||
],
|
||||
});
|
||||
this.logger.info('证书上传成功:crtPath=', crtPath, ',keyPath=', keyPath);
|
||||
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({
|
||||
connectConf,
|
||||
transports: [
|
||||
{
|
||||
localPath: saveCrtPath,
|
||||
remotePath: crtPath,
|
||||
},
|
||||
{
|
||||
localPath: saveKeyPath,
|
||||
remotePath: keyPath,
|
||||
},
|
||||
],
|
||||
});
|
||||
this.logger.info('证书上传成功:crtPath=', crtPath, ',keyPath=', keyPath);
|
||||
}
|
||||
|
||||
//删除临时文件
|
||||
fs.unlinkSync(saveCrtPath);
|
||||
|
|
Loading…
Reference in New Issue