import * as plugins from '../plugins.js'; import type { IChallengeHandler } from './IChallengeHandler.js'; /** * HTTP-01 ACME challenge handler using in-memory storage. * Stores challenge tokens and key authorizations in memory * and serves them via handleRequest for arbitrary HTTP servers. */ export interface Http01MemoryHandlerChallenge { type: string; token: string; keyAuthorization: string; webPath: string; } export class Http01MemoryHandler implements IChallengeHandler { private smartnetworkInstance = new plugins.smartnetwork.SmartNetwork(); private smartdnsClient = new plugins.smartdnsClient.Smartdns({}); private store: Map = new Map(); public getSupportedTypes(): string[] { return ['http-01']; } public async prepare(ch: Http01MemoryHandlerChallenge): Promise { this.store.set(ch.token, ch.keyAuthorization); } public async verify(_ch: Http01MemoryHandlerChallenge): Promise { // No-op return; } public async cleanup(ch: Http01MemoryHandlerChallenge): Promise { this.store.delete(ch.token); } /** * HTTP request handler for serving ACME HTTP-01 challenges. * @param req HTTP request object (should have url property) * @param res HTTP response object * @param next Optional next() callback for Express-style fallthrough */ public handleRequest(req: any, res: any, next?: () => void): void { const url = req.url || ''; const prefix = '/.well-known/acme-challenge/'; if (!url.startsWith(prefix)) { if (next) { return next(); } res.statusCode = 404; return res.end(); } const token = url.slice(prefix.length); const keyAuth = this.store.get(token); if (keyAuth !== undefined) { if (typeof res.status === 'function' && typeof res.send === 'function') { return res.status(200).send(keyAuth); } res.statusCode = 200; res.setHeader('content-type', 'text/plain'); return res.end(keyAuth); } if (next) { return next(); } res.statusCode = 404; return res.end(); } public async checkWetherDomainIsSupported(domainArg: string): Promise { const publicIps = await this.smartnetworkInstance.getPublicIps(); const aRecords = await this.smartdnsClient.getRecordsA(domainArg); const aaaaRecords = await this.smartdnsClient.getRecordsAAAA(domainArg); if (aRecords.length && aRecords.some((record) => record.value !== publicIps.v4)) { return false; } if (aaaaRecords.length && aaaaRecords.some((record) => record.value !== publicIps.v6)) { return false; } return true; } }