perf: http方式校验,选择sftp时,支持修改文件访问权限比如777

pull/330/head
xiaojunnuo 2025-01-20 23:29:03 +08:00
parent ae5dfc3bee
commit 15d6eaf553
9 changed files with 112 additions and 16 deletions

View File

@ -58,7 +58,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
},
required: false,
order: 100,
helper: "PFX、jks格式证书是否加密jks必须设置密码不传则默认123456",
helper: "PFX、jks格式证书是否加密\njks必须设置密码不传则默认123456",
})
pfxPassword!: string;
@ -66,12 +66,17 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
title: "PFX证书转换参数",
value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
component: {
name: "a-input",
name: "a-auto-complete",
vModel: "value",
options: [
{ value: "", label: "兼容 Windows Server 最新" },
{ value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2016" },
{ value: "-nomac -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2008" },
],
},
required: false,
order: 100,
helper: "兼容Server 2016如果导入证书失败请删除此参数",
helper: "兼容Windows Server各个版本",
})
pfxArgs = "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES";

View File

@ -232,7 +232,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
// { value: "ec_521", label: "EC 521" },
],
},
helper: "如无特殊需求,默认即可",
helper: "如无特殊需求,默认即可\n选择RSA 2048 pkcs1可以获得旧版RSA证书",
required: true,
})
privateKeyType!: PrivateKeyType;

View File

@ -8,6 +8,9 @@ export class HttpChallengeUploaderFactory {
} else if (type === "ssh") {
const module = await import("./impls/ssh.js");
return module.SshHttpChallengeUploader;
} else if (type === "sftp") {
const module = await import("./impls/sftp.js");
return module.SftpHttpChallengeUploader;
} else if (type === "ftp") {
const module = await import("./impls/ftp.js");
return module.FtpHttpChallengeUploader;

View File

@ -0,0 +1,51 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { SshAccess, SshClient } from "@certd/plugin-lib";
import path from "path";
import os from "os";
import fs from "fs";
import { SftpAccess } from "@certd/plugin-lib";
export class SftpHttpChallengeUploader extends BaseHttpChallengeUploader<SftpAccess> {
async upload(filePath: string, fileContent: Buffer) {
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
// Write file to temp path
const dir = path.dirname(tmpFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(tmpFilePath, fileContent);
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
const key = this.rootDir + filePath;
try {
const client = new SshClient(this.logger);
await client.uploadFiles({
connectConf: access,
mkdirs: true,
transports: [
{
localPath: tmpFilePath,
remotePath: key,
},
],
opts: {
mode: this.access?.fileMode ?? undefined,
},
});
} finally {
// Remove temp file
fs.unlinkSync(tmpFilePath);
}
}
async remove(filePath: string) {
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
const client = new SshClient(this.logger);
const key = this.rootDir + filePath;
await client.removeFiles({
connectConf: access,
files: [key],
});
}
}

View File

@ -1,2 +1,3 @@
export * from "./ssh.js";
export * from "./ssh-access.js";
export * from "./sftp-access.js";

View File

@ -0,0 +1,34 @@
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
@IsAccess({
name: "sftp",
title: "SFTP授权",
desc: "",
icon: "clarity:host-line",
input: {},
})
export class SftpAccess extends BaseAccess {
@AccessInput({
title: "SSH授权",
component: {
name: "access-selector",
type: "ssh",
vModel: "modelValue",
},
helper: "请选择一个SSH授权",
required: true,
})
sshAccess!: string;
@AccessInput({
title: "文件权限",
component: {
name: "a-input",
vModel: "value",
placeholder: "777",
},
helper: "文件上传后是否修改文件权限",
})
fileMode!: string;
}
new SftpAccess();

View File

@ -80,11 +80,11 @@ export class AsyncSsh2Client {
});
}
async fastPut(options: { sftp: any; localPath: string; remotePath: string }) {
const { sftp, localPath, remotePath } = options;
async fastPut(options: { sftp: any; localPath: string; remotePath: string; opts?: { mode?: number } }) {
const { sftp, localPath, remotePath, opts } = options;
return new Promise((resolve, reject) => {
this.logger.info(`开始上传:${localPath} => ${remotePath}`);
sftp.fastPut(localPath, remotePath, (err: Error) => {
sftp.fastPut(localPath, remotePath, { ...(opts ?? {}) }, (err: Error) => {
if (err) {
reject(err);
this.logger.error("请确认路径是否包含文件名,路径本身不能是目录,路径不能有*?之类的特殊符号,要有写入权限");
@ -255,8 +255,8 @@ export class SshClient {
}
* @param options
*/
async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean }) {
const { connectConf, transports, mkdirs } = options;
async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean; opts?: { mode?: number } }) {
const { connectConf, transports, mkdirs, opts } = options;
await this._call({
connectConf,
callable: async (conn: AsyncSsh2Client) => {
@ -281,7 +281,7 @@ export class SshClient {
}
await conn.exec(mkdirCmd);
}
await conn.fastPut({ sftp, ...transport });
await conn.fastPut({ sftp, ...transport, opts });
}
this.logger.info("文件全部上传成功");
},

View File

@ -30,7 +30,7 @@
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from "vue";
import { Ref, ref, watch, nextTick } from "vue";
import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/type";
import { dict } from "@fast-crud/fast-crud";
@ -62,18 +62,20 @@ watch(
}
);
function onRecordChange() {
async function onRecordChange() {
await nextTick();
emit("update:modelValue", records.value);
emit("change", records.value);
}
const uploaderTypeDict = dict({
data: [
{ label: "SFTP/SSH", value: "ssh" },
{ label: "SFTP", value: "sftp" },
{ label: "FTP", value: "ftp" },
{ label: "阿里云OSS", value: "alioss" },
{ label: "腾讯云COS", value: "tencentcos" },
{ label: "七牛OSS", value: "qiniuoss" }
{ label: "七牛OSS", value: "qiniuoss" },
{ label: "SSH(已废弃请选择SFTP方式)", value: "ssh", disabled: true }
]
});
</script>

View File

@ -5,10 +5,10 @@ import { SshAccess, SshClient } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'uploadCertToHost',
title: '主机-部署证书到主机',
title: '主机-部署证书到SSH主机',
icon: 'line-md:uploading-loop',
group: pluginGroups.host.key,
desc: '上传证书到主机,然后执行部署脚本命令',
desc: 'SFTP上传证书到主机,然后SSH执行部署脚本命令',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,