smartacme/ts/handlers/Http01Handler.ts

54 lines
1.5 KiB
TypeScript

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 <webroot>/.well-known/acme-challenge/.
*/
export interface Http01HandlerOptions {
/**
* Directory that serves HTTP requests for /.well-known/acme-challenge
*/
webroot: string;
}
export class Http01Handler implements IChallengeHandler<{
type: string;
token: string;
keyAuthorization: string;
webPath: string;
}> {
private webroot: string;
constructor(options: Http01HandlerOptions) {
this.webroot = options.webroot;
}
public getSupportedTypes(): string[] {
return ['http-01'];
}
public async prepare(ch: { token: string; keyAuthorization: string; webPath: string }): Promise<void> {
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<void> {
// Optional: implement HTTP polling if desired
return;
}
public async cleanup(ch: { token: string; webPath: string }): Promise<void> {
const relWebPath = ch.webPath.replace(/^\/+/, '');
const filePath = path.join(this.webroot, relWebPath);
try {
await fs.unlink(filePath);
} catch {
// ignore missing file
}
}
}