BREAKING CHANGE(SmartAcme): Refactor challenge handling by removing legacy setChallenge/removeChallenge in favor of pluggable challengeHandlers and update documentation and tests accordingly
This commit is contained in:
40
ts/handlers/Dns01Handler.ts
Normal file
40
ts/handlers/Dns01Handler.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import * as plugins from '../smartacme.plugins.js';
|
||||
import type { IChallengeHandler } from './IChallengeHandler.js';
|
||||
|
||||
/**
|
||||
* DNS-01 challenge handler using CloudflareAccount and Smartdns.
|
||||
*/
|
||||
export class Dns01Handler implements IChallengeHandler<plugins.tsclass.network.IDnsChallenge> {
|
||||
private cf: any;
|
||||
private smartdns: plugins.smartdnsClient.Smartdns;
|
||||
|
||||
constructor(
|
||||
cloudflareAccount: any,
|
||||
smartdnsInstance?: plugins.smartdnsClient.Smartdns,
|
||||
) {
|
||||
this.cf = cloudflareAccount;
|
||||
this.smartdns = smartdnsInstance ?? new plugins.smartdnsClient.Smartdns({});
|
||||
}
|
||||
|
||||
public getSupportedTypes(): string[] {
|
||||
return ['dns-01'];
|
||||
}
|
||||
|
||||
public async prepare(ch: plugins.tsclass.network.IDnsChallenge): Promise<void> {
|
||||
// set DNS TXT record
|
||||
await this.cf.convenience.acmeSetDnsChallenge(ch);
|
||||
// wait for DNS propagation
|
||||
await this.smartdns.checkUntilAvailable(
|
||||
ch.hostName,
|
||||
'TXT',
|
||||
ch.challenge,
|
||||
100,
|
||||
5000,
|
||||
);
|
||||
}
|
||||
|
||||
public async cleanup(ch: plugins.tsclass.network.IDnsChallenge): Promise<void> {
|
||||
// remove DNS TXT record
|
||||
await this.cf.convenience.acmeRemoveDnsChallenge(ch);
|
||||
}
|
||||
}
|
54
ts/handlers/Http01Handler.ts
Normal file
54
ts/handlers/Http01Handler.ts
Normal file
@ -0,0 +1,54 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
22
ts/handlers/IChallengeHandler.ts
Normal file
22
ts/handlers/IChallengeHandler.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Pluggable interface for ACME challenge handlers.
|
||||
* Supports DNS-01, HTTP-01, TLS-ALPN-01, or custom challenge types.
|
||||
*/
|
||||
export interface IChallengeHandler<T> {
|
||||
/**
|
||||
* ACME challenge types this handler supports (e.g. ['dns-01']).
|
||||
*/
|
||||
getSupportedTypes(): string[];
|
||||
/**
|
||||
* Prepare the challenge: set DNS record, start HTTP/TLS server, etc.
|
||||
*/
|
||||
prepare(ch: T): Promise<void>;
|
||||
/**
|
||||
* Optional extra verify step (HTTP GET, ALPN handshake).
|
||||
*/
|
||||
verify?(ch: T): Promise<void>;
|
||||
/**
|
||||
* Clean up resources: remove DNS record, stop server.
|
||||
*/
|
||||
cleanup(ch: T): Promise<void>;
|
||||
}
|
4
ts/handlers/index.ts
Normal file
4
ts/handlers/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export type { IChallengeHandler } from './IChallengeHandler.js';
|
||||
// Removed legacy handler adapter
|
||||
export { Dns01Handler } from './Dns01Handler.js';
|
||||
export { Http01Handler } from './Http01Handler.js';
|
Reference in New Issue
Block a user