BREAKING CHANGE(certs): accept a second eventComms argument in certProvisionFunction, add cert provisioning event types, and emit certificate lifecycle events
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartproxy',
|
||||
version: '24.0.1',
|
||||
version: '25.0.0',
|
||||
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export { SharedRouteManager as RouteManager } from './core/routing/route-manager
|
||||
|
||||
// Export smart-proxy models
|
||||
export type { ISmartProxyOptions, IConnectionRecord, IRouteConfig, IRouteMatch, IRouteAction, IRouteTls, IRouteContext } from './proxies/smart-proxy/models/index.js';
|
||||
export type { TSmartProxyCertProvisionObject } from './proxies/smart-proxy/models/interfaces.js';
|
||||
export type { TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './proxies/smart-proxy/models/interfaces.js';
|
||||
export * from './proxies/smart-proxy/utils/index.js';
|
||||
|
||||
// Original: export * from './smartproxy/classes.pp.snihandler.js'
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
* SmartProxy models
|
||||
*/
|
||||
// Export everything except IAcmeOptions from interfaces
|
||||
export type { ISmartProxyOptions, ISmartProxyCertStore, IConnectionRecord, TSmartProxyCertProvisionObject } from './interfaces.js';
|
||||
export type { ISmartProxyOptions, ISmartProxyCertStore, IConnectionRecord, TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './interfaces.js';
|
||||
export * from './route-types.js';
|
||||
export * from './metrics-types.js';
|
||||
|
||||
@@ -34,6 +34,38 @@ import type { IRouteConfig } from './route-types.js';
|
||||
*/
|
||||
export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
|
||||
|
||||
/**
|
||||
* Communication channel passed as second argument to certProvisionFunction.
|
||||
* Allows the callback to report metadata back to SmartProxy for event emission.
|
||||
*/
|
||||
export interface ICertProvisionEventComms {
|
||||
/** Informational log */
|
||||
log: (message: string) => void;
|
||||
/** Warning (non-fatal) */
|
||||
warn: (message: string) => void;
|
||||
/** Error */
|
||||
error: (message: string) => void;
|
||||
/** Set the certificate expiry date (for the issued event) */
|
||||
setExpiryDate: (date: Date) => void;
|
||||
/** Set the source/method used for provisioning (e.g. 'smartacme-dns-01') */
|
||||
setSource: (source: string) => void;
|
||||
}
|
||||
|
||||
/** Payload for 'certificate-issued' and 'certificate-renewed' events */
|
||||
export interface ICertificateIssuedEvent {
|
||||
domain: string;
|
||||
expiryDate?: string; // ISO 8601
|
||||
source: string; // e.g. 'certProvisionFunction', 'smartacme-dns-01'
|
||||
isRenewal?: boolean;
|
||||
}
|
||||
|
||||
/** Payload for 'certificate-failed' event */
|
||||
export interface ICertificateFailedEvent {
|
||||
domain: string;
|
||||
error: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
// Legacy options and type checking functions have been removed
|
||||
|
||||
/**
|
||||
@@ -140,7 +172,7 @@ export interface ISmartProxyOptions {
|
||||
* Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges,
|
||||
* or a static certificate object for immediate provisioning.
|
||||
*/
|
||||
certProvisionFunction?: (domain: string) => Promise<TSmartProxyCertProvisionObject>;
|
||||
certProvisionFunction?: (domain: string, eventComms: ICertProvisionEventComms) => Promise<TSmartProxyCertProvisionObject>;
|
||||
|
||||
/**
|
||||
* Whether to fallback to ACME if custom certificate provision fails.
|
||||
|
||||
@@ -14,7 +14,7 @@ import { generateDefaultCertificate } from './utils/default-cert-generator.js';
|
||||
import { Mutex } from './utils/mutex.js';
|
||||
|
||||
// Types
|
||||
import type { ISmartProxyOptions, TSmartProxyCertProvisionObject, IAcmeOptions } from './models/interfaces.js';
|
||||
import type { ISmartProxyOptions, TSmartProxyCertProvisionObject, IAcmeOptions, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent } from './models/interfaces.js';
|
||||
import type { IRouteConfig } from './models/route-types.js';
|
||||
import type { IMetrics } from './models/metrics-types.js';
|
||||
|
||||
@@ -420,8 +420,21 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
for (const domain of certDomains) {
|
||||
if (provisionedDomains.has(domain)) continue;
|
||||
provisionedDomains.add(domain);
|
||||
|
||||
// Build eventComms channel for this domain
|
||||
let expiryDate: string | undefined;
|
||||
let source = 'certProvisionFunction';
|
||||
|
||||
const eventComms: ICertProvisionEventComms = {
|
||||
log: (msg) => logger.log('info', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
||||
warn: (msg) => logger.log('warn', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
||||
error: (msg) => logger.log('error', `[certProvision ${domain}] ${msg}`, { component: 'smart-proxy' }),
|
||||
setExpiryDate: (date) => { expiryDate = date.toISOString(); },
|
||||
setSource: (s) => { source = s; },
|
||||
};
|
||||
|
||||
try {
|
||||
const result: TSmartProxyCertProvisionObject = await provisionFn(domain);
|
||||
const result: TSmartProxyCertProvisionObject = await provisionFn(domain, eventComms);
|
||||
|
||||
if (result === 'http01') {
|
||||
// Callback wants HTTP-01 for this domain — trigger Rust ACME explicitly
|
||||
@@ -455,10 +468,24 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
logger.log('warn', `certStore.save() failed for ${domain}: ${storeErr.message}`, { component: 'smart-proxy' });
|
||||
}
|
||||
}
|
||||
|
||||
// Emit certificate-issued event
|
||||
this.emit('certificate-issued', {
|
||||
domain,
|
||||
expiryDate: expiryDate || (certObj.validUntil ? new Date(certObj.validUntil).toISOString() : undefined),
|
||||
source,
|
||||
} satisfies ICertificateIssuedEvent);
|
||||
}
|
||||
} catch (err: any) {
|
||||
logger.log('warn', `certProvisionFunction failed for ${domain}: ${err.message}`, { component: 'smart-proxy' });
|
||||
|
||||
// Emit certificate-failed event
|
||||
this.emit('certificate-failed', {
|
||||
domain,
|
||||
error: err.message,
|
||||
source,
|
||||
} satisfies ICertificateFailedEvent);
|
||||
|
||||
// Fallback to ACME if enabled and route has a name
|
||||
if (this.settings.certProvisionFallbackToAcme !== false && route.name) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user