mirror of https://github.com/certd/certd
perf: 触发证书重新申请input变化对比规则优化,减少升级版本后触发申请证书的情况
parent
84fd3b250d
commit
c46a2a9a39
|
@ -181,6 +181,9 @@ export class RunnableCollection {
|
||||||
if (runnable?.status) {
|
if (runnable?.status) {
|
||||||
runnable.status.status = ResultType.none;
|
runnable.status.status = ResultType.none;
|
||||||
runnable.status.result = ResultType.none;
|
runnable.status.result = ResultType.none;
|
||||||
|
runnable.status.inputHash = "";
|
||||||
|
// @ts-ignore
|
||||||
|
runnable.input = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,6 @@ export type HistoryResultGroup = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type HistoryResult = {
|
export type HistoryResult = {
|
||||||
// input: any;
|
|
||||||
inputHash?: string;
|
inputHash?: string;
|
||||||
output: any;
|
output: any;
|
||||||
files?: FileItem[];
|
files?: FileItem[];
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { CertReader } from "./cert-reader.js";
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
import { CertConverter } from "./convert.js";
|
import { CertConverter } from "./convert.js";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import { pick } from "lodash-es";
|
||||||
|
|
||||||
export { CertReader };
|
export { CertReader };
|
||||||
export type { CertInfo };
|
export type { CertInfo };
|
||||||
|
@ -203,10 +204,35 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputChanged = this.ctx.inputChanged;
|
let inputChanged = this.ctx.inputChanged;
|
||||||
if (inputChanged) {
|
if (inputChanged) {
|
||||||
this.logger.info("输入参数变更,准备申请新证书");
|
this.logger.info("input hash 有变更,检查是否需要重新申请证书");
|
||||||
return null;
|
//判断域名有没有变更
|
||||||
|
/**
|
||||||
|
* "renewDays": 20,
|
||||||
|
* "certApplyPlugin": "CertApply",
|
||||||
|
* "sslProvider": "letsencrypt",
|
||||||
|
* "privateKeyType": "rsa_2048_pkcs1",
|
||||||
|
* "dnsProviderType": "aliyun",
|
||||||
|
* "domains": [
|
||||||
|
* "*.handsfree.work"
|
||||||
|
* ],
|
||||||
|
* "email": "xiaojunnuo@qq.com",
|
||||||
|
* "dnsProviderAccess": 3,
|
||||||
|
* "useProxy": false,
|
||||||
|
* "skipLocalVerify": false,
|
||||||
|
* "successNotify": true,
|
||||||
|
* "pfxPassword": "123456"
|
||||||
|
*/
|
||||||
|
const checkInputChanges = ["domains", "sslProvider", "privateKeyType", "dnsProviderType", "dnsProviderAccess", "pfxPassword"];
|
||||||
|
const oldInput = JSON.stringify(pick(this.lastStatus?.input, checkInputChanges));
|
||||||
|
const thisInput = JSON.stringify(pick(this, checkInputChanges));
|
||||||
|
inputChanged = oldInput !== thisInput;
|
||||||
|
|
||||||
|
if (inputChanged) {
|
||||||
|
this.logger.info("输入参数变更,准备申请新证书");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldCert: CertReader | undefined = undefined;
|
let oldCert: CertReader | undefined = undefined;
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi
|
||||||
import { useUserStore } from "/@/store/modules/user";
|
import { useUserStore } from "/@/store/modules/user";
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
import { useSettingStore } from "/@/store/modules/settings";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { DoVerify } from "./api";
|
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -125,9 +124,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
onDictChange: ({ form, dict }) => {
|
onDictChange: ({ form, dict }: any) => {
|
||||||
if (!form.cnameProviderId) {
|
if (!form.cnameProviderId) {
|
||||||
const item = dict.data.find((item) => item.isDefault);
|
const item = dict.data.find((item: any) => item.isDefault);
|
||||||
if (item) {
|
if (item) {
|
||||||
form.cnameProviderId = item.id;
|
form.cnameProviderId = item.id;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +179,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
message.success("验证成功");
|
message.success("验证成功");
|
||||||
row.status = "valid";
|
row.status = "valid";
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
message.error(e.message);
|
message.error(e.message);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div>{{ item.name }}({{ item.fileName }})</div>
|
<div>{{ item.name }}({{ item.fileName }})</div>
|
||||||
<fs-copyable :model-value="item.content" :button="{ show: false }">
|
<fs-copyable :model-value="item.content" :button="{ show: false }">
|
||||||
<a-tag type="success">复制</a-tag>
|
<a-tag color="success">复制</a-tag>
|
||||||
</fs-copyable>
|
</fs-copyable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -155,7 +155,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||||
const viewCert = async (row: any) => {
|
const viewCert = async (row: any) => {
|
||||||
const cert = await api.GetCert(row.id);
|
const cert = await api.GetCert(row.id);
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
notification.error({ message: "还没有产生证书,请先运行流水线" });
|
notification.error({ message: "请先运行一次流水线" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
<fs-icon
|
<fs-icon
|
||||||
v-if="!editMode"
|
v-if="!editMode"
|
||||||
class="pointer color-blue ml-2"
|
class="pointer color-blue ml-2"
|
||||||
title="重新运行此步骤"
|
title="完全重新运行此步骤"
|
||||||
icon="SyncOutlined"
|
icon="SyncOutlined"
|
||||||
@click="run(item.id)"
|
@click="run(item.id)"
|
||||||
></fs-icon>
|
></fs-icon>
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"koa-send": "^5.0.1",
|
"koa-send": "^5.0.1",
|
||||||
"kubernetes-client": "^9.0.0",
|
"kubernetes-client": "^9.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
|
|
@ -102,7 +102,7 @@ export class AsyncSsh2Client {
|
||||||
let iconv: any = await import('iconv-lite');
|
let iconv: any = await import('iconv-lite');
|
||||||
iconv = iconv.default;
|
iconv = iconv.default;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.logger.info(`执行命令:[${this.connConf.host}][exec]: ` + script);
|
this.logger.info(`执行命令:[${this.connConf.host}][exec]: \n` + script);
|
||||||
this.conn.exec(script, (err: Error, stream: any) => {
|
this.conn.exec(script, (err: Error, stream: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@ -274,7 +274,7 @@ export class SshClient {
|
||||||
let { script } = options;
|
let { script } = options;
|
||||||
const { connectConf } = options;
|
const { connectConf } = options;
|
||||||
|
|
||||||
this.logger.info('命令:', script);
|
// this.logger.info('命令:', script);
|
||||||
return await this._call({
|
return await this._call({
|
||||||
connectConf,
|
connectConf,
|
||||||
callable: async (conn: AsyncSsh2Client) => {
|
callable: async (conn: AsyncSsh2Client) => {
|
||||||
|
|
|
@ -4,6 +4,8 @@ import path from 'path';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { SshAccess, SshClient } from '../../plugin-host/index.js';
|
import { SshAccess, SshClient } from '../../plugin-host/index.js';
|
||||||
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
|
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
|
||||||
|
import JSZip from 'jszip';
|
||||||
|
import * as os from 'node:os';
|
||||||
|
|
||||||
const defaultBackupDir = 'certd_backup';
|
const defaultBackupDir = 'certd_backup';
|
||||||
const defaultFilePrefix = 'db-backup';
|
const defaultFilePrefix = 'db-backup';
|
||||||
|
@ -100,16 +102,25 @@ export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('当前备份方式:', this.backupMode);
|
//本地压缩
|
||||||
|
const zip = new JSZip();
|
||||||
|
zip.file(dbPath);
|
||||||
|
const content = await zip.generateAsync({ type: 'nodebuffer' });
|
||||||
|
const dbZipFilename = `${this.filePrefix}.${dayjs().format('YYYYMMDD.HHmmss')}.sqlite.zip`;
|
||||||
|
const dbZipPath = path.resolve(os.tmpdir(), dbZipFilename);
|
||||||
|
await fs.promises.writeFile(dbZipPath, content);
|
||||||
|
this.logger.info(`数据库文件压缩完成:${dbZipPath}`);
|
||||||
|
|
||||||
|
this.logger.info('开始备份,当前备份方式:', this.backupMode);
|
||||||
const backupDir = this.backupDir || defaultBackupDir;
|
const backupDir = this.backupDir || defaultBackupDir;
|
||||||
const backupFile = `${backupDir}/${this.filePrefix}.${dayjs().format('YYYYMMDD.HHmmss')}.sqlite`;
|
const backupFilePath = `${backupDir}/${dbZipFilename}`;
|
||||||
|
|
||||||
if (this.backupMode === 'local') {
|
if (this.backupMode === 'local') {
|
||||||
await this.localBackup(dbPath, backupDir, backupFile);
|
await this.localBackup(dbPath, backupDir, backupFilePath);
|
||||||
} else if (this.backupMode === 'ssh') {
|
} else if (this.backupMode === 'ssh') {
|
||||||
await this.sshBackup(dbPath, backupDir, backupFile);
|
await this.sshBackup(dbPath, backupDir, backupFilePath);
|
||||||
} else if (this.backupMode === 'oss') {
|
} else if (this.backupMode === 'oss') {
|
||||||
await this.ossBackup(dbPath, backupDir, backupFile);
|
await this.ossBackup(dbPath, backupDir, backupFilePath);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`不支持的备份方式:${this.backupMode}`);
|
throw new Error(`不支持的备份方式:${this.backupMode}`);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue