mirror of https://github.com/certd/certd
perf: 优化证书申请成功率
parent
453f1baa0b
commit
968c4690a0
|
@ -137,7 +137,12 @@ module.exports = async (client, userOpts) => {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log(`[auto] [${d}] Running challenge verification`);
|
log(`[auto] [${d}] Running challenge verification`);
|
||||||
await client.verifyChallenge(authz, challenge);
|
try {
|
||||||
|
await client.verifyChallenge(authz, challenge);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
log(`[auto] [${d}] challenge verification threw error: ${e.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Complete challenge and wait for valid status */
|
/* Complete challenge and wait for valid status */
|
||||||
|
@ -170,11 +175,41 @@ module.exports = async (client, userOpts) => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const domainSets = [];
|
||||||
|
|
||||||
const challengePromises = authorizations.map((authz) => async () => {
|
authorizations.forEach((authz) => {
|
||||||
await challengeFunc(authz);
|
const d = authz.identifier.value;
|
||||||
|
let setd = false;
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const group of domainSets) {
|
||||||
|
if (!group[d]) {
|
||||||
|
group[d] = authz;
|
||||||
|
setd = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!setd) {
|
||||||
|
const group = {};
|
||||||
|
group[d] = authz;
|
||||||
|
domainSets.push(group);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const allChallengePromises = [];
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const domainSet of domainSets) {
|
||||||
|
const challengePromises = [];
|
||||||
|
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
||||||
|
for (const domain in domainSet) {
|
||||||
|
const authz = domainSet[domain];
|
||||||
|
challengePromises.push(async () => {
|
||||||
|
log(`[auto] [${domain}] Starting challenge`);
|
||||||
|
await challengeFunc(authz);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
allChallengePromises.push(challengePromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`[auto] challengeGroups:${allChallengePromises.length}`);
|
||||||
function runAllPromise(tasks) {
|
function runAllPromise(tasks) {
|
||||||
let promise = Promise.resolve();
|
let promise = Promise.resolve();
|
||||||
tasks.forEach((task) => {
|
tasks.forEach((task) => {
|
||||||
|
@ -195,9 +230,15 @@ module.exports = async (client, userOpts) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log('开始challenge');
|
log(`开始challenge,共${allChallengePromises.length}组`);
|
||||||
await runPromisePa(challengePromises);
|
let i = 0;
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const challengePromises of allChallengePromises) {
|
||||||
|
i += 1;
|
||||||
|
log(`开始第${i}组`);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await runPromisePa(challengePromises);
|
||||||
|
}
|
||||||
log('challenge结束');
|
log('challenge结束');
|
||||||
|
|
||||||
// log('[auto] Waiting for challenge valid status');
|
// log('[auto] Waiting for challenge valid status');
|
||||||
|
|
|
@ -55,7 +55,7 @@ class HttpClient {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async request(url, method, opts = {}) {
|
async request(url, method, opts = {}) {
|
||||||
if (this.urlMapping && this.urlMapping.enabled === true && this.urlMapping.mappings) {
|
if (this.urlMapping && this.urlMapping.mappings) {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
for (const key in this.urlMapping.mappings) {
|
for (const key in this.urlMapping.mappings) {
|
||||||
if (url.includes(key)) {
|
if (url.includes(key)) {
|
||||||
|
|
|
@ -72,7 +72,7 @@ async function spawn(opts: SpawnOption): Promise<string> {
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
return safePromise((resolve, reject) => {
|
return safePromise((resolve, reject) => {
|
||||||
const ls = childProcess.spawn(cmd, {
|
const ls = childProcess.spawn(cmd, {
|
||||||
shell: process.platform == "win32",
|
shell: true,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
...opts.env,
|
...opts.env,
|
||||||
|
|
|
@ -13,7 +13,7 @@ export type CertInfo = {
|
||||||
key: string;
|
key: string;
|
||||||
csr: string;
|
csr: string;
|
||||||
};
|
};
|
||||||
export type SSLProvider = "letsencrypt" | "buypass" | "zerossl";
|
export type SSLProvider = "letsencrypt" | "google" | "zerossl";
|
||||||
type AcmeServiceOptions = {
|
type AcmeServiceOptions = {
|
||||||
userContext: IContext;
|
userContext: IContext;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
|
@ -42,10 +42,18 @@ export class AcmeService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccountConfig(email: string): Promise<any> {
|
async getAccountConfig(email: string, urlMapping: UrlMapping): Promise<any> {
|
||||||
const conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
|
const conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
|
||||||
if (conf.accountUrl?.indexOf("letsencrypt.proxy.handsfree.work")) {
|
if (urlMapping && urlMapping.mappings) {
|
||||||
conf.accountUrl = conf.accountUrl.replace("letsencrypt.proxy.handsfree.work", "acme-v02.api.letsencrypt.org");
|
for (const key in urlMapping.mappings) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(urlMapping.mappings, key)) {
|
||||||
|
const element = urlMapping.mappings[key];
|
||||||
|
if (conf.accountUrl?.indexOf(element) > -1) {
|
||||||
|
//如果用了代理url,要替换回去
|
||||||
|
conf.accountUrl = conf.accountUrl.replace(element, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +67,14 @@ export class AcmeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAcmeClient(email: string, isTest = false): Promise<acme.Client> {
|
async getAcmeClient(email: string, isTest = false): Promise<acme.Client> {
|
||||||
const conf = await this.getAccountConfig(email);
|
const urlMapping: UrlMapping = {
|
||||||
|
enabled: false,
|
||||||
|
mappings: {
|
||||||
|
"acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
|
||||||
|
"dv.acme-v02.api.pki.goog": "google.proxy.handsfree.work",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const conf = await this.getAccountConfig(email, urlMapping);
|
||||||
if (conf.key == null) {
|
if (conf.key == null) {
|
||||||
conf.key = await this.createNewKey();
|
conf.key = await this.createNewKey();
|
||||||
await this.saveAccountConfig(email, conf);
|
await this.saveAccountConfig(email, conf);
|
||||||
|
@ -70,19 +85,15 @@ export class AcmeService {
|
||||||
} else {
|
} else {
|
||||||
directoryUrl = acme.directory[this.sslProvider].production;
|
directoryUrl = acme.directory[this.sslProvider].production;
|
||||||
}
|
}
|
||||||
const urlMapping: UrlMapping = { enabled: false, mappings: {} };
|
|
||||||
if (this.options.useMappingProxy) {
|
if (this.options.useMappingProxy) {
|
||||||
urlMapping.enabled = true;
|
urlMapping.enabled = true;
|
||||||
urlMapping.mappings = {
|
|
||||||
"acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const client = new acme.Client({
|
const client = new acme.Client({
|
||||||
directoryUrl: directoryUrl,
|
directoryUrl: directoryUrl,
|
||||||
accountKey: conf.key,
|
accountKey: conf.key,
|
||||||
accountUrl: conf.accountUrl,
|
accountUrl: conf.accountUrl,
|
||||||
externalAccountBinding: this.eab,
|
externalAccountBinding: this.eab,
|
||||||
backoffAttempts: 30,
|
backoffAttempts: 15,
|
||||||
backoffMin: 5000,
|
backoffMin: 5000,
|
||||||
backoffMax: 10000,
|
backoffMax: 10000,
|
||||||
urlMapping,
|
urlMapping,
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
options: [
|
options: [
|
||||||
{ value: "letsencrypt", label: "Let's Encrypt" },
|
{ value: "letsencrypt", label: "Let's Encrypt" },
|
||||||
// { value: "letsencrypt-proxy", label: "Let's Encrypt代理,letsencrypt.org无法访问时使用" },
|
// { value: "letsencrypt-proxy", label: "Let's Encrypt代理,letsencrypt.org无法访问时使用" },
|
||||||
// { value: "buypass", label: "Buypass" },
|
{ value: "google", label: "Google" },
|
||||||
{ value: "zerossl", label: "ZeroSSL" },
|
{ value: "zerossl", label: "ZeroSSL" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
<a-input v-model:value="testFormState.receiver" />
|
<a-input v-model:value="testFormState.receiver" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||||
<a-button type="primary" html-type="submit">测试</a-button>
|
<a-button type="primary" :loading="testFormState.loading" html-type="submit">测试</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,3 +6,6 @@ typeorm:
|
||||||
default:
|
default:
|
||||||
logging: false
|
logging: false
|
||||||
|
|
||||||
|
|
||||||
|
plus:
|
||||||
|
serverBaseUrl: 'https://api.ai.handsfree.work/'
|
||||||
|
|
|
@ -88,6 +88,9 @@ const development = {
|
||||||
system: {
|
system: {
|
||||||
resetAdminPasswd: false,
|
resetAdminPasswd: false,
|
||||||
},
|
},
|
||||||
|
plus: {
|
||||||
|
serverBaseUrl: 'http://127.0.0.1:11007',
|
||||||
|
},
|
||||||
} as MidwayConfig;
|
} as MidwayConfig;
|
||||||
mergeConfig(development, 'development');
|
mergeConfig(development, 'development');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue