mirror of
https://github.com/certd/certd.git
synced 2026-04-14 12:30:54 +08:00
feat: ui模式
BREAKING CHANGE: 接口配置变更
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
// src/decorator/memoryCache.decorator.ts
|
||||
import { dnsProviderRegistry } from "./registry";
|
||||
import { DnsProviderDefine } from "./api";
|
||||
import { Decorator, AUTOWIRE_KEY } from "@certd/pipeline";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Registry } from "@certd/pipeline";
|
||||
|
||||
// @ts-ignore
|
||||
export const dnsProviderRegistry = new Registry();
|
||||
|
||||
@@ -5,7 +5,11 @@ import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||
import { Logger } from "log4js";
|
||||
import { IContext } from "@certd/pipeline/src/core/context";
|
||||
import { IDnsProvider } from "../../dns-provider";
|
||||
|
||||
export type CertInfo = {
|
||||
crt: string;
|
||||
key: string;
|
||||
csr: string;
|
||||
};
|
||||
export class AcmeService {
|
||||
userContext: IContext;
|
||||
logger: Logger;
|
||||
@@ -166,7 +170,7 @@ export class AcmeService {
|
||||
},
|
||||
});
|
||||
|
||||
const cert = {
|
||||
const cert: CertInfo = {
|
||||
crt: crt.toString(),
|
||||
key: key.toString(),
|
||||
csr: csr.toString(),
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { CertInfo } from "./acme";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import forge from "node-forge";
|
||||
import path from "path";
|
||||
export class CertReader implements CertInfo {
|
||||
crt: string;
|
||||
key: string;
|
||||
csr: string;
|
||||
|
||||
detail: any;
|
||||
expires: number;
|
||||
constructor(certInfo: CertInfo) {
|
||||
this.crt = certInfo.crt;
|
||||
this.key = certInfo.key;
|
||||
this.csr = certInfo.csr;
|
||||
|
||||
const { detail, expires } = this.getCrtDetail(this.crt);
|
||||
this.detail = detail;
|
||||
this.expires = expires.getTime();
|
||||
}
|
||||
|
||||
toCertInfo(): CertInfo {
|
||||
return {
|
||||
crt: this.crt,
|
||||
key: this.key,
|
||||
csr: this.csr,
|
||||
};
|
||||
}
|
||||
|
||||
getCrtDetail(crt: string) {
|
||||
const pki = forge.pki;
|
||||
const detail = pki.certificateFromPem(crt.toString());
|
||||
const expires = detail.validity.notAfter;
|
||||
return { detail, expires };
|
||||
}
|
||||
|
||||
saveToFile(type: "crt" | "key", filepath?: string) {
|
||||
if (filepath == null) {
|
||||
//写入临时目录
|
||||
filepath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + "", `cert.${type}`);
|
||||
}
|
||||
|
||||
const dir = path.dirname(filepath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(filepath, this[type]);
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,15 @@
|
||||
import { Autowire, HttpClient, IAccessService, IContext, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||
import forge from "node-forge";
|
||||
import dayjs from "dayjs";
|
||||
import { AcmeService } from "./acme";
|
||||
import { AcmeService, CertInfo } from "./acme";
|
||||
import _ from "lodash";
|
||||
import { Logger } from "log4js";
|
||||
import { Decorator } from "@certd/pipeline/src/decorator";
|
||||
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
export class CertInfo {
|
||||
crt: string;
|
||||
key: string;
|
||||
csr: string;
|
||||
import { CertReader } from "./cert-reader";
|
||||
|
||||
detail: any;
|
||||
expires: number;
|
||||
constructor(opts: { crt: string; key: string; csr: string }) {
|
||||
this.crt = opts.crt;
|
||||
this.key = opts.key;
|
||||
this.csr = opts.csr;
|
||||
export { CertReader };
|
||||
export type { CertInfo };
|
||||
|
||||
const { detail, expires } = this.getCrtDetail(this.crt);
|
||||
this.detail = detail;
|
||||
this.expires = expires.getTime();
|
||||
}
|
||||
|
||||
getCrtDetail(crt: string) {
|
||||
const pki = forge.pki;
|
||||
const detail = pki.certificateFromPem(crt.toString());
|
||||
const expires = detail.validity.notAfter;
|
||||
return { detail, expires };
|
||||
}
|
||||
|
||||
saveToFile(type: "crt" | "key", path?: string) {
|
||||
if (path == null) {
|
||||
//写入临时目录
|
||||
path = `${os.tmpdir()}/certd/tmp/${Math.floor(Math.random() * 1000000)}/cert.${type}`;
|
||||
}
|
||||
fs.writeFileSync(path, this[type]);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@IsTaskPlugin({
|
||||
name: "CertApply",
|
||||
title: "证书申请",
|
||||
@@ -166,10 +135,14 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||
return this.output(oldCert);
|
||||
}
|
||||
const cert = await this.doCertApply();
|
||||
return this.output(cert);
|
||||
if (cert != null) {
|
||||
this.output(cert.toCertInfo());
|
||||
} else {
|
||||
throw new Error("申请证书失败");
|
||||
}
|
||||
}
|
||||
|
||||
output(cert: any) {
|
||||
output(cert: CertInfo) {
|
||||
this.cert = cert;
|
||||
}
|
||||
|
||||
@@ -187,12 +160,12 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||
const oldInputStr = await this.pipelineContext.getObj(inputCacheKey);
|
||||
await this.pipelineContext.setObj(inputCacheKey, this.domains);
|
||||
const oldInput = JSON.stringify(oldInputStr);
|
||||
const thisInput = JSON.stringify(this.cert);
|
||||
const thisInput = JSON.stringify(this.domains);
|
||||
if (oldInput !== thisInput) {
|
||||
inputChanged = true;
|
||||
}
|
||||
|
||||
let oldCert;
|
||||
let oldCert: CertReader | undefined = undefined;
|
||||
try {
|
||||
oldCert = await this.readCurrentCert();
|
||||
} catch (e) {
|
||||
@@ -257,10 +230,7 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||
await this.writeCert(cert);
|
||||
const ret = await this.readCurrentCert();
|
||||
|
||||
return {
|
||||
...ret,
|
||||
isNew: true,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
formatCert(pem: string) {
|
||||
@@ -271,7 +241,7 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||
}
|
||||
|
||||
async writeCert(cert: { crt: string; key: string; csr: string }) {
|
||||
const newCert = {
|
||||
const newCert: CertInfo = {
|
||||
crt: this.formatCert(cert.crt),
|
||||
key: this.formatCert(cert.key),
|
||||
csr: this.formatCert(cert.csr),
|
||||
@@ -282,12 +252,12 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||
await this.pipelineContext.set("cert.csr", newCert.csr);
|
||||
}
|
||||
|
||||
async readCurrentCert() {
|
||||
const cert: any = await this.pipelineContext.getObj("cert");
|
||||
async readCurrentCert(): Promise<CertReader | undefined> {
|
||||
const cert: CertInfo = await this.pipelineContext.getObj("cert");
|
||||
if (cert == null) {
|
||||
return undefined;
|
||||
}
|
||||
return new CertInfo(cert);
|
||||
return new CertReader(cert);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./cert-plugin";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "commonjs",
|
||||
|
||||
@@ -42,6 +42,10 @@ export class SshAccess implements IAccess {
|
||||
@AccessInput({
|
||||
title: "密钥",
|
||||
helper: "密钥或密码必填一项",
|
||||
component: {
|
||||
name: "a-textarea",
|
||||
vModel: "value",
|
||||
},
|
||||
})
|
||||
privateKey!: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Autowire, IAccessService, IsTaskPlugin, ILogger, RunStrategy, TaskInput, ITaskPlugin } from "@certd/pipeline";
|
||||
import { Autowire, IAccessService, ILogger, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { SshClient } from "../../lib/ssh";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: "hostShellExecute",
|
||||
@@ -24,15 +23,6 @@ export class HostShellExecutePlugin implements ITaskPlugin {
|
||||
required: true,
|
||||
})
|
||||
accessId!: string;
|
||||
@TaskInput({
|
||||
title: "域名证书",
|
||||
helper: "请选择前置任务输出的域名证书",
|
||||
component: {
|
||||
name: "pi-output-selector",
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
@TaskInput({
|
||||
title: "shell脚本命令",
|
||||
component: {
|
||||
@@ -51,7 +41,7 @@ export class HostShellExecutePlugin implements ITaskPlugin {
|
||||
async onInstance() {}
|
||||
async execute(): Promise<void> {
|
||||
const { script, accessId } = this;
|
||||
const connectConf = this.accessService.getById(accessId);
|
||||
const connectConf = await this.accessService.getById(accessId);
|
||||
const sshClient = new SshClient(this.logger);
|
||||
const ret = await sshClient.exec({
|
||||
connectConf,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||
import { SshClient } from "../../lib/ssh";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
import { CertInfo, CertReader } from "@certd/plugin-cert";
|
||||
import * as fs from "fs";
|
||||
|
||||
@IsTaskPlugin({
|
||||
@@ -67,11 +67,12 @@ export class UploadCertToHostPlugin implements ITaskPlugin {
|
||||
async onInstance() {}
|
||||
async execute(): Promise<void> {
|
||||
const { crtPath, keyPath, cert, accessId, sudo } = this;
|
||||
const connectConf = this.accessService.getById(accessId);
|
||||
const certReader = new CertReader(cert);
|
||||
const connectConf = await this.accessService.getById(accessId);
|
||||
const sshClient = new SshClient(this.logger);
|
||||
|
||||
const saveCrtPath = cert.saveToFile("crt");
|
||||
const saveKeyPath = cert.saveToFile("key");
|
||||
const saveCrtPath = certReader.saveToFile("crt");
|
||||
const saveKeyPath = certReader.saveToFile("key");
|
||||
|
||||
await sshClient.uploadFiles({
|
||||
connectConf,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "commonjs",
|
||||
|
||||
@@ -73,7 +73,7 @@ export class HuaweiDnsProvider implements IDnsProvider {
|
||||
if (records && records.length > 0) {
|
||||
for (const record of records) {
|
||||
await this.removeRecord({
|
||||
record: records[0],
|
||||
record,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @ts-ignore
|
||||
import signer from "./signer";
|
||||
import https from "https";
|
||||
import { HuaweiAccess } from "../access";
|
||||
import { axios } from "@certd/acme-client";
|
||||
import { logger } from "@certd/pipeline";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "commonjs",
|
||||
|
||||
Reference in New Issue
Block a user