diff --git a/packages/dashmate/src/listr/tasks/ssl/VerificationServer.js b/packages/dashmate/src/listr/tasks/ssl/VerificationServer.js index 0e7e584f35..5f19b26408 100644 --- a/packages/dashmate/src/listr/tasks/ssl/VerificationServer.js +++ b/packages/dashmate/src/listr/tasks/ssl/VerificationServer.js @@ -3,8 +3,14 @@ import path from 'path'; import dots from 'dot'; import os from 'os'; import { TEMPLATES_DIR } from '../../../constants.js'; +import wait from '../../../util/wait.js'; export default class VerificationServer { + /** + * @param {string} verification url + */ + #validationUrl; + /** * * @param {Docker} docker @@ -35,6 +41,8 @@ export default class VerificationServer { throw new Error('Server is already setup'); } + this.#validationUrl = validationUrl; + this.config = config; dots.templateSettings.strip = false; @@ -160,6 +168,31 @@ export default class VerificationServer { this.container = null; } + async waitForServerIsResponding() { + const MAX_WAIT_TIME = 10000; // Maximum wait time in milliseconds + const INTERVAL = 500; // Interval to check in milliseconds + const FETCH_TIMEOUT = 2000; // Timeout for each fetch in ms + const startTime = Date.now(); + + while (Date.now() - startTime < MAX_WAIT_TIME) { + try { + const response = await fetch( + this.#validationUrl, + { signal: AbortSignal.timeout(FETCH_TIMEOUT) }, + ); + if (response.ok) { + return true; + } + } catch (e) { + // Ignore errors and continue retrying + } + + await wait(INTERVAL); + } + + return false; + } + /** * Destroy verification server files * diff --git a/packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js b/packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js index 0cecce0fc5..4636b37069 100644 --- a/packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js +++ b/packages/dashmate/src/listr/tasks/ssl/zerossl/obtainZeroSSLCertificateTaskFactory.js @@ -166,15 +166,26 @@ export default function obtainZeroSSLCertificateTaskFactory( { title: 'Start verification server', skip: (ctx) => ctx.certificate && !['pending_validation', 'draft'].includes(ctx.certificate.status), - task: async () => verificationServer.start(), + task: async (ctx) => { + await verificationServer.start(); + + const isResponding = await verificationServer.waitForServerIsResponding(); + + if (!isResponding) { + throw new Error(`Verification server is not responding. +Please ensure that port 80 on your public IP address ${ctx.externalIp} is open +for incoming HTTP connections. You may need to configure your firewall to +ensure this port is accessible from the public internet. If you are using +Network Address Translation (NAT), please enable port forwarding for port 80 +and all Dash service ports listed above.`); + } + }, }, { title: 'Verify certificate IP address', skip: (ctx) => ctx.certificate && !['pending_validation', 'draft'].includes(ctx.certificate.status), task: async (ctx, task) => { let retry; - let autoRetryCount = 0; - const MAX_AUTO_RETRIES = 3; // Adjust based on requirements do { try { await verifyDomain(ctx.certificate.id, ctx.apiKey); @@ -189,38 +200,37 @@ export default function obtainZeroSSLCertificateTaskFactory( } } - if (e.type === 'domain_control_validation_failed') { - // Retry on this undocumented error whatever it means - if (autoRetryCount >= MAX_AUTO_RETRIES) { - throw e; - } - autoRetryCount++; - if (process.env.DEBUG) { - // eslint-disable-next-line no-console - console.warn(`Retry ${autoRetryCount}/${MAX_AUTO_RETRIES} verification due to domain_control_validation_failed error`); - } - await wait(5000); - } else { - if (ctx.noRetry !== true) { - retry = await task.prompt({ - type: 'toggle', - header: chalk` An error occurred during verification: {red ${e.message}} + // If retry is disabled, throw the error + // or prompt the user to retry + if (ctx.noRetry !== true) { + let errorMessage = e.message; - Please ensure that port 80 on your public IP address ${ctx.externalIp} is open - for incoming HTTP connections. You may need to configure your firewall to - ensure this port is accessible from the public internet. If you are using - Network Address Translation (NAT), please enable port forwarding for port 80 - and all Dash service ports listed above.`, - message: 'Try again?', - enabled: 'Yes', - disabled: 'No', - initial: true, - }); + // Get the error message from details if it exists + if (e.type === 'domain_control_validation_failed' && e.details[ctx.externalIp]) { + const errorDetails = Object.values(e.details[ctx.externalIp])[0]; + if (errorDetails?.error) { + errorMessage = errorDetails.error_info; + } } - if (!retry) { - throw e; - } + retry = await task.prompt({ + type: 'toggle', + header: chalk` An error occurred during verification: {red ${errorMessage}} + + Please ensure that port 80 on your public IP address ${ctx.externalIp} is open + for incoming HTTP connections. You may need to configure your firewall to + ensure this port is accessible from the public internet. If you are using + Network Address Translation (NAT), please enable port forwarding for port 80 + and all Dash service ports listed above.`, + message: 'Try again?', + enabled: 'Yes', + disabled: 'No', + initial: true, + }); + } + + if (!retry) { + throw e; } } } while (retry);