feat(handlers): Add in-memory HTTP-01 challenge handler and rename file-based handler to Http01Webroot
This commit is contained in:
@ -6,14 +6,14 @@ 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 {
|
||||
export interface Http01WebrootOptions {
|
||||
/**
|
||||
* Directory that serves HTTP requests for /.well-known/acme-challenge
|
||||
*/
|
||||
webroot: string;
|
||||
}
|
||||
|
||||
export class Http01Handler implements IChallengeHandler<{
|
||||
export class Http01Webroot implements IChallengeHandler<{
|
||||
type: string;
|
||||
token: string;
|
||||
keyAuthorization: string;
|
||||
@ -21,7 +21,7 @@ export class Http01Handler implements IChallengeHandler<{
|
||||
}> {
|
||||
private webroot: string;
|
||||
|
||||
constructor(options: Http01HandlerOptions) {
|
||||
constructor(options: Http01WebrootOptions) {
|
||||
this.webroot = options.webroot;
|
||||
}
|
||||
|
||||
|
67
ts/handlers/Http01MemoryHandler.ts
Normal file
67
ts/handlers/Http01MemoryHandler.ts
Normal file
@ -0,0 +1,67 @@
|
||||
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<Http01MemoryHandlerChallenge> {
|
||||
private store: Map<string, string> = new Map();
|
||||
|
||||
public getSupportedTypes(): string[] {
|
||||
return ['http-01'];
|
||||
}
|
||||
|
||||
public async prepare(ch: Http01MemoryHandlerChallenge): Promise<void> {
|
||||
this.store.set(ch.token, ch.keyAuthorization);
|
||||
}
|
||||
|
||||
public async verify(_ch: Http01MemoryHandlerChallenge): Promise<void> {
|
||||
// No-op
|
||||
return;
|
||||
}
|
||||
|
||||
public async cleanup(ch: Http01MemoryHandlerChallenge): Promise<void> {
|
||||
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();
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export type { IChallengeHandler } from './IChallengeHandler.js';
|
||||
// Removed legacy handler adapter
|
||||
export { Dns01Handler } from './Dns01Handler.js';
|
||||
export { Http01Handler } from './Http01Handler.js';
|
||||
export { Http01Webroot } from './Http01Handler.js';
|
||||
export { Http01MemoryHandler } from './Http01MemoryHandler.js';
|
Reference in New Issue
Block a user