import { promises as fs } from 'fs'; import * as path from 'path'; import type { IChallengeHandler } from './IChallengeHandler.js'; /** * HTTP-01 ACME challenge handler using file-system webroot. * Writes and removes the challenge file under /.well-known/acme-challenge/. */ export interface Http01WebrootOptions { /** * Directory that serves HTTP requests for /.well-known/acme-challenge */ webroot: string; } export class Http01Webroot implements IChallengeHandler<{ type: string; token: string; keyAuthorization: string; webPath: string; }> { private webroot: string; constructor(options: Http01WebrootOptions) { this.webroot = options.webroot; } public getSupportedTypes(): string[] { return ['http-01']; } public async prepare(ch: { token: string; keyAuthorization: string; webPath: string }): Promise { const relWebPath = ch.webPath.replace(/^\/+/, ''); const filePath = path.join(this.webroot, relWebPath); const dir = path.dirname(filePath); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(filePath, ch.keyAuthorization, 'utf8'); } public async verify(ch: { webPath: string; keyAuthorization: string }): Promise { // Optional: implement HTTP polling if desired return; } public async cleanup(ch: { token: string; webPath: string }): Promise { const relWebPath = ch.webPath.replace(/^\/+/, ''); const filePath = path.join(this.webroot, relWebPath); try { await fs.unlink(filePath); } catch { // ignore missing file } } }