perf: 上传到主机插件支持复制到本机路径

pull/68/head
xiaojunnuo 2024-05-30 10:54:18 +08:00
parent d9eb927b0a
commit 92446c3399
7 changed files with 107 additions and 29 deletions

View File

@ -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 dayjs from "dayjs";
import { AcmeService, CertInfo } from "./acme"; import {AcmeService, CertInfo} from "./acme";
import _ from "lodash"; import _ from "lodash";
import { Logger } from "log4js"; 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}`);
} }
/** /**

View File

@ -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;
}

View File

@ -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;
} }

View File

@ -64,7 +64,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
} }
}, },
rowHandle: { rowHandle: {
width: "150px" width: "180px"
}, },
table: { table: {
rowSelection: { rowSelection: {

View File

@ -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,

View File

@ -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);
} }
} }

View File

@ -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,29 +87,46 @@ 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');
await sshClient.uploadFiles({ if (this.copyToThisHost) {
connectConf, this.copyFile(saveCrtPath, crtPath);
transports: [ this.copyFile(saveKeyPath, keyPath);
{ this.logger.info('证书复制成功crtPath=', crtPath, ',keyPath=', keyPath);
localPath: saveCrtPath, } else {
remotePath: crtPath, if (!accessId) {
}, throw new Error('主机登录授权配置不能为空');
{ }
localPath: saveKeyPath, const connectConf = await this.accessService.getById(accessId);
remotePath: keyPath, const sshClient = new SshClient(this.logger);
}, await sshClient.uploadFiles({
], connectConf,
}); transports: [
this.logger.info('证书上传成功crtPath=', crtPath, ',keyPath=', keyPath); {
localPath: saveCrtPath,
remotePath: crtPath,
},
{
localPath: saveKeyPath,
remotePath: keyPath,
},
],
});
this.logger.info('证书上传成功crtPath=', crtPath, ',keyPath=', keyPath);
}
//删除临时文件 //删除临时文件
fs.unlinkSync(saveCrtPath); fs.unlinkSync(saveCrtPath);