mirror of https://github.com/certd/certd
fix: 修复成功后跳过之后丢失腾讯云证书id的bug
parent
3345c145b8
commit
37eb762afe
|
@ -144,7 +144,6 @@ module.exports = async (client, userOpts) => {
|
|||
log(`[auto] [${d}] challenge verification threw error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/* Complete challenge and wait for valid status */
|
||||
log(`[auto] [${d}] Completing challenge with ACME provider and waiting for valid status`);
|
||||
await client.completeChallenge(challenge);
|
||||
|
@ -236,6 +235,9 @@ module.exports = async (client, userOpts) => {
|
|||
for (const challengePromises of allChallengePromises) {
|
||||
i += 1;
|
||||
log(`开始第${i}组`);
|
||||
if (opts.signal && opts.signal.aborted) {
|
||||
throw new Error('用户取消');
|
||||
}
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await runPromisePa(challengePromises);
|
||||
}
|
||||
|
|
|
@ -490,6 +490,9 @@ class AcmeClient {
|
|||
const keyAuthorization = await this.getChallengeKeyAuthorization(challenge);
|
||||
|
||||
const verifyFn = async () => {
|
||||
if (this.opts.signal && this.opts.signal.aborted) {
|
||||
throw new Error('用户取消');
|
||||
}
|
||||
await verify[challenge.type](authz, challenge, keyAuthorization);
|
||||
};
|
||||
|
||||
|
@ -513,6 +516,9 @@ class AcmeClient {
|
|||
*/
|
||||
|
||||
async completeChallenge(challenge) {
|
||||
if (this.opts.signal && this.opts.signal.aborted) {
|
||||
throw new Error('用户取消');
|
||||
}
|
||||
const resp = await this.api.completeChallenge(challenge.url, {});
|
||||
return resp.data;
|
||||
}
|
||||
|
@ -550,6 +556,10 @@ class AcmeClient {
|
|||
}
|
||||
|
||||
const verifyFn = async (abort) => {
|
||||
if (this.opts.signal && this.opts.signal.aborted) {
|
||||
throw new Error('用户取消');
|
||||
}
|
||||
|
||||
const resp = await this.api.apiRequest(item.url, null, [200]);
|
||||
|
||||
/* Verify status */
|
||||
|
|
|
@ -45,6 +45,7 @@ export interface ClientOptions {
|
|||
backoffMin?: number;
|
||||
backoffMax?: number;
|
||||
urlMapping?: UrlMapping;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export interface ClientExternalAccountBindingOptions {
|
||||
|
@ -61,6 +62,7 @@ export interface ClientAutoOptions {
|
|||
skipChallengeVerification?: boolean;
|
||||
challengePriority?: string[];
|
||||
preferredChain?: string;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export class Client {
|
||||
|
|
|
@ -23,6 +23,7 @@ export type ExecutorOptions = {
|
|||
emailService: IEmailService;
|
||||
fileRootDir?: string;
|
||||
};
|
||||
|
||||
export class Executor {
|
||||
pipeline: Pipeline;
|
||||
runtime!: RunHistory;
|
||||
|
@ -32,7 +33,8 @@ export class Executor {
|
|||
lastStatusMap!: RunnableCollection;
|
||||
lastRuntime!: RunHistory;
|
||||
options: ExecutorOptions;
|
||||
canceled = false;
|
||||
abort: AbortController = new AbortController();
|
||||
|
||||
onChanged: (history: RunHistory) => Promise<void>;
|
||||
constructor(options: ExecutorOptions) {
|
||||
this.options = options;
|
||||
|
@ -53,7 +55,7 @@ export class Executor {
|
|||
}
|
||||
|
||||
async cancel() {
|
||||
this.canceled = true;
|
||||
this.abort.abort();
|
||||
this.runtime?.cancel(this.pipeline);
|
||||
await this.onChanged(this.runtime);
|
||||
}
|
||||
|
@ -102,6 +104,8 @@ export class Executor {
|
|||
}
|
||||
}
|
||||
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
|
||||
runnable.status!.output = lastNode?.status?.output;
|
||||
runnable.status!.files = lastNode?.status?.files;
|
||||
this.runtime.skip(runnable);
|
||||
await this.onChanged(this.runtime);
|
||||
return ResultType.skip;
|
||||
|
@ -113,10 +117,15 @@ export class Executor {
|
|||
|
||||
// const timeout = runnable.timeout ?? 20 * 60 * 1000;
|
||||
try {
|
||||
if (this.canceled) {
|
||||
if (this.abort.signal.aborted) {
|
||||
this.runtime.cancel(runnable);
|
||||
return ResultType.canceled;
|
||||
}
|
||||
await run();
|
||||
if (this.abort.signal.aborted) {
|
||||
this.runtime.cancel(runnable);
|
||||
return ResultType.canceled;
|
||||
}
|
||||
this.runtime.success(runnable);
|
||||
return ResultType.success;
|
||||
} catch (e: any) {
|
||||
|
@ -211,16 +220,11 @@ export class Executor {
|
|||
if (item.component?.name === "pi-output-selector") {
|
||||
const contextKey = step.input[key];
|
||||
if (contextKey != null) {
|
||||
const value = this.runtime.context[contextKey];
|
||||
if (value == null) {
|
||||
currentLogger.warn(`[step init] input ${define.title} is null,前置任务步骤输出值为空,请按如下步骤排查:`);
|
||||
currentLogger.log(`1、检查前置任务(证书申请任务)是否是配置了成功后跳过,如果是,请改为正常执行`);
|
||||
currentLogger.log(
|
||||
`2、是否曾经删除过前置任务(证书申请任务),然后又重新添加了,如果是,请重新编辑当前任务,重新选择一下前置任务输出的参数(域名证书那一栏)`
|
||||
);
|
||||
currentLogger.log(`3、以上都不是,联系作者吧`);
|
||||
}
|
||||
step.input[key] = value;
|
||||
// "cert": "step.-BNFVPMKPu2O-i9NiOQxP.cert",
|
||||
const arr = contextKey.split(".");
|
||||
const id = arr[1];
|
||||
const outputKey = arr[2];
|
||||
step.input[key] = this.lastStatusMap.get(id)?.status?.output[outputKey];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -241,6 +245,7 @@ export class Executor {
|
|||
parent: this.runtime.id,
|
||||
rootDir: this.options.fileRootDir,
|
||||
}),
|
||||
signal: this.abort.signal,
|
||||
};
|
||||
instance.setCtx(taskCtx);
|
||||
|
||||
|
@ -254,8 +259,8 @@ export class Executor {
|
|||
//输出上下文变量到output context
|
||||
_.forEach(define.output, (item: any, key: any) => {
|
||||
step.status!.output[key] = instance[key];
|
||||
const stepOutputKey = `step.${step.id}.${key}`;
|
||||
this.runtime.context[stepOutputKey] = instance[key];
|
||||
// const stepOutputKey = `step.${step.id}.${key}`;
|
||||
// this.runtime.context[stepOutputKey] = instance[key];
|
||||
});
|
||||
step.status!.files = instance.getFiles();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Context, HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../dt/index.js";
|
||||
import { HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../dt/index.js";
|
||||
import _ from "lodash-es";
|
||||
import { buildLogger } from "../utils/util.log.js";
|
||||
import { Logger } from "log4js";
|
||||
|
@ -14,15 +14,12 @@ export type RunTrigger = {
|
|||
|
||||
export function NewRunHistory(obj: any) {
|
||||
const history = new RunHistory(obj.id, obj.trigger, obj.pipeline);
|
||||
history.context = obj.context;
|
||||
history.logs = obj.logs;
|
||||
history._loggers = obj.loggers;
|
||||
return history;
|
||||
}
|
||||
export class RunHistory {
|
||||
id!: string;
|
||||
//运行时上下文变量
|
||||
context: Context = {};
|
||||
pipeline!: Pipeline;
|
||||
logs: {
|
||||
[runnableId: string]: string[];
|
||||
|
|
|
@ -64,6 +64,7 @@ export type TaskInstanceContext = {
|
|||
http: AxiosInstance;
|
||||
fileStore: FileStore;
|
||||
lastStatus?: Runnable;
|
||||
signal: AbortSignal;
|
||||
};
|
||||
|
||||
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
||||
|
|
|
@ -23,6 +23,7 @@ type AcmeServiceOptions = {
|
|||
skipLocalVerify?: boolean;
|
||||
useMappingProxy?: boolean;
|
||||
privateKeyType?: PrivateKeyType;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
export class AcmeService {
|
||||
|
@ -99,6 +100,7 @@ export class AcmeService {
|
|||
backoffMin: 5000,
|
||||
backoffMax: 10000,
|
||||
urlMapping,
|
||||
signal: this.options.signal,
|
||||
});
|
||||
|
||||
if (conf.accountUrl == null) {
|
||||
|
@ -253,6 +255,7 @@ export class AcmeService {
|
|||
challengeRemoveFn: async (authz: acme.Authorization, challenge: Challenge, keyAuthorization: string, recordItem: any): Promise<any> => {
|
||||
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider);
|
||||
},
|
||||
signal: this.options.signal,
|
||||
});
|
||||
|
||||
const cert: CertInfo = {
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
"svg-captcha": "^1.4.0",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44",
|
||||
"tencentcloud-sdk-nodejs-dnspod": "^4.0.866",
|
||||
"tencentcloud-sdk-nodejs-teo": "^4.0.919",
|
||||
"typeorm": "^0.3.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -4,9 +4,8 @@ import { TencentAccess } from '../../access/index.js';
|
|||
import { CertInfo } from '@certd/plugin-cert';
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: 'DeployCertToTencentEO',
|
||||
title: '部署到腾讯云EO',
|
||||
desc: '腾讯云边缘安全加速平台 EO',
|
||||
name: 'DeployCertToTencentCDN',
|
||||
title: '部署到腾讯云CDN',
|
||||
group: pluginGroups.tencent.key,
|
||||
default: {
|
||||
strategy: {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
||||
import tencentcloud from 'tencentcloud-sdk-nodejs';
|
||||
import tencentcloud from 'tencentcloud-sdk-nodejs-teo';
|
||||
import { TencentAccess } from '../../access/index.js';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: 'DeployCertToTencentCDN',
|
||||
title: '部署到腾讯云CDN',
|
||||
name: 'DeployCertToTencentEO',
|
||||
title: '部署到腾讯云EO',
|
||||
desc: '腾讯云边缘安全加速平台EO,必须配置上传证书到腾讯云任务',
|
||||
group: pluginGroups.tencent.key,
|
||||
default: {
|
||||
strategy: {
|
||||
|
@ -13,17 +13,17 @@ import { CertInfo } from '@certd/plugin-cert';
|
|||
},
|
||||
},
|
||||
})
|
||||
export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
||||
export class DeployToEOPlugin extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
helper: '请选择前置任务输出的域名证书',
|
||||
title: '已上传证书ID',
|
||||
helper: '请选择前置任务上传到腾讯云的证书',
|
||||
component: {
|
||||
name: 'pi-output-selector',
|
||||
from: 'CertApply',
|
||||
from: 'UploadCertToTencent',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
certId!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: 'Access提供者',
|
||||
|
@ -36,6 +36,12 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
|||
})
|
||||
accessId!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '站点ID',
|
||||
helper: '类似于zone-xxxx的字符串,在站点概览页面左上角,或者,站点列表页面站点名称下方',
|
||||
})
|
||||
zoneId!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '证书名称',
|
||||
helper: '证书上传后将以此参数作为名称前缀',
|
||||
|
@ -44,9 +50,16 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
|||
|
||||
@TaskInput({
|
||||
title: 'cdn加速域名',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
mode: 'tags',
|
||||
open: false,
|
||||
},
|
||||
helper: '支持多个域名',
|
||||
rules: [{ required: true, message: '该项必填' }],
|
||||
})
|
||||
domainName!: string;
|
||||
domainNames!: string[];
|
||||
|
||||
// @TaskInput({
|
||||
// title: "CDN接口",
|
||||
|
@ -69,7 +82,7 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
|||
}
|
||||
|
||||
getClient(accessProvider: TencentAccess) {
|
||||
const CdnClient = tencentcloud.cdn.v20180606.Client;
|
||||
const TeoClient = tencentcloud.teo.v20220901.Client;
|
||||
|
||||
const clientConfig = {
|
||||
credential: {
|
||||
|
@ -79,31 +92,31 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
|||
region: '',
|
||||
profile: {
|
||||
httpProfile: {
|
||||
endpoint: 'cdn.tencentcloudapi.com',
|
||||
endpoint: 'teo.tencentcloudapi.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return new CdnClient(clientConfig);
|
||||
return new TeoClient(clientConfig);
|
||||
}
|
||||
|
||||
buildParams() {
|
||||
return {
|
||||
Https: {
|
||||
Switch: 'on',
|
||||
CertInfo: {
|
||||
Certificate: this.cert.crt,
|
||||
PrivateKey: this.cert.key,
|
||||
ZoneId: this.zoneId,
|
||||
Hosts: this.domainNames,
|
||||
Mode: 'sslcert',
|
||||
ServerCertInfo: [
|
||||
{
|
||||
CertId: this.certId,
|
||||
},
|
||||
},
|
||||
Domain: this.domainName,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async doRequest(client: any, params: any) {
|
||||
const ret = await client.UpdateDomainConfig(params);
|
||||
const ret = await client.ModifyHostsCertificate(params);
|
||||
this.checkRet(ret);
|
||||
this.logger.info('设置腾讯云CDN证书成功:', ret.RequestId);
|
||||
this.logger.info('设置腾讯云EO证书成功:', ret.RequestId);
|
||||
return ret.RequestId;
|
||||
}
|
||||
|
Loading…
Reference in New Issue