fix: 修复并发情况下证书申请日志混乱的bug

v2-dev
xiaojunnuo 2025-10-15 23:03:59 +08:00
parent 54c42b1fc2
commit bb2714ff24
7 changed files with 29 additions and 21 deletions

View File

@ -28,7 +28,6 @@ class AcmeApi {
} }
} }
} }
console.log(locationUrl, mapping);
return locationUrl; return locationUrl;
} }

View File

@ -2,7 +2,6 @@
* ACME auto helper * ACME auto helper
*/ */
import { readCsrDomains } from "./crypto/index.js"; import { readCsrDomains } from "./crypto/index.js";
import { log } from "./logger.js";
import { wait } from "./wait.js"; import { wait } from "./wait.js";
import { CancelError } from "./error.js"; import { CancelError } from "./error.js";
@ -45,6 +44,9 @@ export default async (client, userOpts) => {
accountPayload.externalAccountBinding = opts.externalAccountBinding; accountPayload.externalAccountBinding = opts.externalAccountBinding;
} }
const log = (...args)=>{
return client.logger.info(...args);
}
/** /**
* Register account * Register account
*/ */

View File

@ -3,9 +3,9 @@
*/ */
import axios from 'axios'; import axios from 'axios';
import { parseRetryAfterHeader } from './util.js'; import { parseRetryAfterHeader } from './util.js';
import { log } from './logger.js';
const { AxiosError } = axios; const { AxiosError } = axios;
import {getGlobalAgents, HttpError} from '@certd/basic' import {getGlobalAgents, HttpError} from '@certd/basic'
import { log } from './logger.js';
/** /**
* Defaults * Defaults
*/ */

View File

@ -5,7 +5,6 @@
*/ */
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { getPemBodyAsB64u } from './crypto/index.js'; import { getPemBodyAsB64u } from './crypto/index.js';
import { log } from './logger.js';
import HttpClient from './http.js'; import HttpClient from './http.js';
import AcmeApi from './api.js'; import AcmeApi from './api.js';
import verify from './verify.js'; import verify from './verify.js';
@ -104,8 +103,13 @@ class AcmeClient {
max: this.opts.backoffMax, max: this.opts.backoffMax,
}; };
this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding, this.opts.urlMapping); this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding, this.opts.urlMapping, opts.logger);
this.api = new AcmeApi(this.http, this.opts.accountUrl); this.api = new AcmeApi(this.http, this.opts.accountUrl);
this.logger = opts.logger;
}
log(...args) {
this.logger.info(...args);
} }
/** /**
@ -177,7 +181,7 @@ class AcmeClient {
this.getAccountUrl(); this.getAccountUrl();
/* Account URL exists */ /* Account URL exists */
log('Account URL exists, returning updateAccount()'); this.log('Account URL exists, returning updateAccount()');
return this.updateAccount(data); return this.updateAccount(data);
} }
catch (e) { catch (e) {
@ -185,7 +189,7 @@ class AcmeClient {
/* HTTP 200: Account exists */ /* HTTP 200: Account exists */
if (resp.status === 200) { if (resp.status === 200) {
log('Account already exists (HTTP 200), returning updateAccount()'); this.log('Account already exists (HTTP 200), returning updateAccount()');
return this.updateAccount(data); return this.updateAccount(data);
} }
@ -214,7 +218,7 @@ class AcmeClient {
this.api.getAccountUrl(); this.api.getAccountUrl();
} }
catch (e) { catch (e) {
log('No account URL found, returning createAccount()'); this.log('No account URL found, returning createAccount()');
return this.createAccount(data); return this.createAccount(data);
} }
@ -502,7 +506,7 @@ class AcmeClient {
await verify[challenge.type](authz, challenge, keyAuthorization); await verify[challenge.type](authz, challenge, keyAuthorization);
}; };
log('Waiting for ACME challenge verification等待ACME检查验证'); this.log('Waiting for ACME challenge verification等待ACME检查验证');
return util.retry(verifyFn, this.backoffOpts); return util.retry(verifyFn, this.backoffOpts);
} }
@ -570,7 +574,7 @@ class AcmeClient {
const resp = await this.api.apiRequest(item.url, null, [200]); const resp = await this.api.apiRequest(item.url, null, [200]);
/* Verify status */ /* Verify status */
log(`[${d}] Item has status检查状态: ${resp.data.status}`); this.log(`[${d}] Item has status检查状态: ${resp.data.status}`);
if (invalidStates.includes(resp.data.status)) { if (invalidStates.includes(resp.data.status)) {
abort(); abort();
@ -586,7 +590,7 @@ class AcmeClient {
throw new Error(`[${d}] Unexpected item status: ${resp.data.status}`); throw new Error(`[${d}] Unexpected item status: ${resp.data.status}`);
}; };
log(`[${d}] Waiting for valid status 等待valid状态: ${item.url}`, this.backoffOpts); this.log(`[${d}] Waiting for valid status 等待valid状态: ${item.url}`, this.backoffOpts);
return util.retry(verifyFn, this.backoffOpts); return util.retry(verifyFn, this.backoffOpts);
} }

View File

@ -19,7 +19,7 @@ import { getJwk } from './crypto/index.js';
*/ */
class HttpClient { class HttpClient {
constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {}) { constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {},logger) {
this.directoryUrl = directoryUrl; this.directoryUrl = directoryUrl;
this.accountKey = accountKey; this.accountKey = accountKey;
this.externalAccountBinding = externalAccountBinding; this.externalAccountBinding = externalAccountBinding;
@ -31,6 +31,7 @@ class HttpClient {
this.directoryMaxAge = 86400; this.directoryMaxAge = 86400;
this.directoryTimestamp = 0; this.directoryTimestamp = 0;
this.urlMapping = urlMapping; this.urlMapping = urlMapping;
this.log = logger? logger.info.bind(logger) : log;
} }
/** /**
@ -48,7 +49,7 @@ class HttpClient {
for (const key in this.urlMapping.mappings) { for (const key in this.urlMapping.mappings) {
if (url.includes(key)) { if (url.includes(key)) {
const newUrl = url.replace(key, this.urlMapping.mappings[key]); const newUrl = url.replace(key, this.urlMapping.mappings[key]);
log(`use reverse proxy: ${newUrl}`); this.log(`use reverse proxy: ${newUrl}`);
url = newUrl; url = newUrl;
} }
} }
@ -65,10 +66,10 @@ class HttpClient {
opts.headers['Content-Type'] = 'application/jose+json'; opts.headers['Content-Type'] = 'application/jose+json';
/* Request */ /* Request */
log(`HTTP request: ${method} ${url}`); this.log(`HTTP request: ${method} ${url}`);
const resp = await axios.request(opts); const resp = await axios.request(opts);
log(`RESP ${resp.status} ${method} ${url}`); this.log(`RESP ${resp.status} ${method} ${url}`);
return resp; return resp;
} }
@ -85,7 +86,7 @@ class HttpClient {
const age = (now - this.directoryTimestamp); const age = (now - this.directoryTimestamp);
if (!this.directoryCache || (age > this.directoryMaxAge)) { if (!this.directoryCache || (age > this.directoryMaxAge)) {
log(`Refreshing ACME directory, age: ${age}`); this.log(`Refreshing ACME directory, age: ${age}`);
const resp = await this.request(this.directoryUrl, 'get'); const resp = await this.request(this.directoryUrl, 'get');
if (resp.status >= 400) { if (resp.status >= 400) {
@ -187,7 +188,7 @@ class HttpClient {
/* Nonce */ /* Nonce */
if (nonce) { if (nonce) {
log(`Using nonce: ${nonce}`); this.log(`Using nonce: ${nonce}`);
header.nonce = nonce; header.nonce = nonce;
} }
@ -314,7 +315,7 @@ class HttpClient {
nonce = resp.headers['replay-nonce'] || null; nonce = resp.headers['replay-nonce'] || null;
attempts += 1; attempts += 1;
log(`Caught invalid nonce error, retrying (${attempts}/${this.maxBadNonceRetries}) signed request to: ${url}`); this.log(`Caught invalid nonce error, retrying (${attempts}/${this.maxBadNonceRetries}) signed request to: ${url}`);
return this.signedRequest(url, payload, { kid, nonce, includeExternalAccountBinding }, attempts); return this.signedRequest(url, payload, { kid, nonce, includeExternalAccountBinding }, attempts);
} }

View File

@ -49,6 +49,7 @@ export interface ClientOptions {
backoffMax?: number; backoffMax?: number;
urlMapping?: UrlMapping; urlMapping?: UrlMapping;
signal?: AbortSignal; signal?: AbortSignal;
logger?:any
} }
export interface ClientExternalAccountBindingOptions { export interface ClientExternalAccountBindingOptions {

View File

@ -82,9 +82,9 @@ export class AcmeService {
this.sslProvider = options.sslProvider || "letsencrypt"; this.sslProvider = options.sslProvider || "letsencrypt";
this.eab = options.eab; this.eab = options.eab;
this.skipLocalVerify = options.skipLocalVerify ?? false; this.skipLocalVerify = options.skipLocalVerify ?? false;
acme.setLogger((message: any, ...args: any[]) => { // acme.setLogger((message: any, ...args: any[]) => {
this.logger.info(message, ...args); // this.logger.info(message, ...args);
}); // });
} }
async getAccountConfig(email: string, urlMapping: UrlMapping): Promise<any> { async getAccountConfig(email: string, urlMapping: UrlMapping): Promise<any> {
@ -155,6 +155,7 @@ export class AcmeService {
backoffMax: 10000, backoffMax: 10000,
urlMapping, urlMapping,
signal: this.options.signal, signal: this.options.signal,
logger: this.logger,
}); });
if (conf.accountUrl == null) { if (conf.accountUrl == null) {