54 lines
1.5 KiB
TypeScript
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
|
|
}
|
|
}
|
|
} |