376 lines
10 KiB
TypeScript
376 lines
10 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import type {
|
|
ICertificateData,
|
|
ICertificateFailure,
|
|
ICertificateExpiring
|
|
} from '../models/common-types.js';
|
|
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
import { Port80HandlerEvents } from '../models/common-types.js';
|
|
|
|
/**
|
|
* Standardized event names used throughout the system
|
|
*/
|
|
export enum ProxyEvents {
|
|
// Certificate events
|
|
CERTIFICATE_ISSUED = 'certificate:issued',
|
|
CERTIFICATE_RENEWED = 'certificate:renewed',
|
|
CERTIFICATE_FAILED = 'certificate:failed',
|
|
CERTIFICATE_EXPIRING = 'certificate:expiring',
|
|
|
|
// Component lifecycle events
|
|
COMPONENT_STARTED = 'component:started',
|
|
COMPONENT_STOPPED = 'component:stopped',
|
|
|
|
// Connection events
|
|
CONNECTION_ESTABLISHED = 'connection:established',
|
|
CONNECTION_CLOSED = 'connection:closed',
|
|
CONNECTION_ERROR = 'connection:error',
|
|
|
|
// Request events
|
|
REQUEST_RECEIVED = 'request:received',
|
|
REQUEST_COMPLETED = 'request:completed',
|
|
REQUEST_ERROR = 'request:error',
|
|
|
|
// Route events
|
|
ROUTE_MATCHED = 'route:matched',
|
|
ROUTE_UPDATED = 'route:updated',
|
|
ROUTE_ERROR = 'route:error',
|
|
|
|
// Security events
|
|
SECURITY_BLOCKED = 'security:blocked',
|
|
SECURITY_BREACH_ATTEMPT = 'security:breach-attempt',
|
|
|
|
// TLS events
|
|
TLS_HANDSHAKE_STARTED = 'tls:handshake-started',
|
|
TLS_HANDSHAKE_COMPLETED = 'tls:handshake-completed',
|
|
TLS_HANDSHAKE_FAILED = 'tls:handshake-failed'
|
|
}
|
|
|
|
/**
|
|
* Component types for event metadata
|
|
*/
|
|
export enum ComponentType {
|
|
SMART_PROXY = 'smart-proxy',
|
|
NETWORK_PROXY = 'network-proxy',
|
|
NFTABLES_PROXY = 'nftables-proxy',
|
|
PORT80_HANDLER = 'port80-handler',
|
|
CERTIFICATE_MANAGER = 'certificate-manager',
|
|
ROUTE_MANAGER = 'route-manager',
|
|
CONNECTION_MANAGER = 'connection-manager',
|
|
TLS_MANAGER = 'tls-manager',
|
|
SECURITY_MANAGER = 'security-manager'
|
|
}
|
|
|
|
/**
|
|
* Base event data interface
|
|
*/
|
|
export interface IEventData {
|
|
timestamp: number;
|
|
componentType: ComponentType;
|
|
componentId?: string;
|
|
}
|
|
|
|
/**
|
|
* Certificate event data
|
|
*/
|
|
export interface ICertificateEventData extends IEventData, ICertificateData {
|
|
isRenewal?: boolean;
|
|
source?: string;
|
|
}
|
|
|
|
/**
|
|
* Certificate failure event data
|
|
*/
|
|
export interface ICertificateFailureEventData extends IEventData, ICertificateFailure {}
|
|
|
|
/**
|
|
* Certificate expiring event data
|
|
*/
|
|
export interface ICertificateExpiringEventData extends IEventData, ICertificateExpiring {}
|
|
|
|
/**
|
|
* Component lifecycle event data
|
|
*/
|
|
export interface IComponentEventData extends IEventData {
|
|
name: string;
|
|
version?: string;
|
|
}
|
|
|
|
/**
|
|
* Connection event data
|
|
*/
|
|
export interface IConnectionEventData extends IEventData {
|
|
connectionId: string;
|
|
clientIp: string;
|
|
serverIp?: string;
|
|
port: number;
|
|
isTls?: boolean;
|
|
domain?: string;
|
|
}
|
|
|
|
/**
|
|
* Request event data
|
|
*/
|
|
export interface IRequestEventData extends IEventData {
|
|
connectionId: string;
|
|
requestId: string;
|
|
method?: string;
|
|
path?: string;
|
|
statusCode?: number;
|
|
duration?: number;
|
|
routeId?: string;
|
|
routeName?: string;
|
|
}
|
|
|
|
/**
|
|
* Route event data
|
|
*/
|
|
export interface IRouteEventData extends IEventData {
|
|
route: IRouteConfig;
|
|
context?: any;
|
|
}
|
|
|
|
/**
|
|
* Security event data
|
|
*/
|
|
export interface ISecurityEventData extends IEventData {
|
|
clientIp: string;
|
|
reason: string;
|
|
routeId?: string;
|
|
routeName?: string;
|
|
}
|
|
|
|
/**
|
|
* TLS event data
|
|
*/
|
|
export interface ITlsEventData extends IEventData {
|
|
connectionId: string;
|
|
domain?: string;
|
|
clientIp: string;
|
|
tlsVersion?: string;
|
|
cipherSuite?: string;
|
|
sniHostname?: string;
|
|
}
|
|
|
|
/**
|
|
* Logger interface for event system
|
|
*/
|
|
export interface IEventLogger {
|
|
info: (message: string, ...args: any[]) => void;
|
|
warn: (message: string, ...args: any[]) => void;
|
|
error: (message: string, ...args: any[]) => void;
|
|
debug?: (message: string, ...args: any[]) => void;
|
|
}
|
|
|
|
/**
|
|
* Event handler type
|
|
*/
|
|
export type EventHandler<T> = (data: T) => void;
|
|
|
|
/**
|
|
* Helper class to standardize event emission and handling
|
|
* across all system components
|
|
*/
|
|
export class EventSystem {
|
|
private emitter: plugins.EventEmitter;
|
|
private componentType: ComponentType;
|
|
private componentId: string;
|
|
private logger?: IEventLogger;
|
|
|
|
constructor(
|
|
componentType: ComponentType,
|
|
componentId: string = '',
|
|
logger?: IEventLogger
|
|
) {
|
|
this.emitter = new plugins.EventEmitter();
|
|
this.componentType = componentType;
|
|
this.componentId = componentId;
|
|
this.logger = logger;
|
|
}
|
|
|
|
/**
|
|
* Emit a certificate issued event
|
|
*/
|
|
public emitCertificateIssued(data: Omit<ICertificateEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: ICertificateEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.info?.(`Certificate issued for ${data.domain}`);
|
|
this.emitter.emit(ProxyEvents.CERTIFICATE_ISSUED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a certificate renewed event
|
|
*/
|
|
public emitCertificateRenewed(data: Omit<ICertificateEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: ICertificateEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.info?.(`Certificate renewed for ${data.domain}`);
|
|
this.emitter.emit(ProxyEvents.CERTIFICATE_RENEWED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a certificate failed event
|
|
*/
|
|
public emitCertificateFailed(data: Omit<ICertificateFailureEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: ICertificateFailureEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.error?.(`Certificate issuance failed for ${data.domain}: ${data.error}`);
|
|
this.emitter.emit(ProxyEvents.CERTIFICATE_FAILED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a certificate expiring event
|
|
*/
|
|
public emitCertificateExpiring(data: Omit<ICertificateExpiringEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: ICertificateExpiringEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.warn?.(`Certificate expiring for ${data.domain} in ${data.daysRemaining} days`);
|
|
this.emitter.emit(ProxyEvents.CERTIFICATE_EXPIRING, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a component started event
|
|
*/
|
|
public emitComponentStarted(name: string, version?: string): void {
|
|
const eventData: IComponentEventData = {
|
|
name,
|
|
version,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.info?.(`Component ${name} started${version ? ` (v${version})` : ''}`);
|
|
this.emitter.emit(ProxyEvents.COMPONENT_STARTED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a component stopped event
|
|
*/
|
|
public emitComponentStopped(name: string): void {
|
|
const eventData: IComponentEventData = {
|
|
name,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.info?.(`Component ${name} stopped`);
|
|
this.emitter.emit(ProxyEvents.COMPONENT_STOPPED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a connection established event
|
|
*/
|
|
public emitConnectionEstablished(data: Omit<IConnectionEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: IConnectionEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.debug?.(`Connection ${data.connectionId} established from ${data.clientIp} on port ${data.port}`);
|
|
this.emitter.emit(ProxyEvents.CONNECTION_ESTABLISHED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a connection closed event
|
|
*/
|
|
public emitConnectionClosed(data: Omit<IConnectionEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: IConnectionEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.debug?.(`Connection ${data.connectionId} closed`);
|
|
this.emitter.emit(ProxyEvents.CONNECTION_CLOSED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Emit a route matched event
|
|
*/
|
|
public emitRouteMatched(data: Omit<IRouteEventData, 'timestamp' | 'componentType' | 'componentId'>): void {
|
|
const eventData: IRouteEventData = {
|
|
...data,
|
|
timestamp: Date.now(),
|
|
componentType: this.componentType,
|
|
componentId: this.componentId
|
|
};
|
|
|
|
this.logger?.debug?.(`Route matched: ${data.route.name || data.route.id || 'unnamed'}`);
|
|
this.emitter.emit(ProxyEvents.ROUTE_MATCHED, eventData);
|
|
}
|
|
|
|
/**
|
|
* Subscribe to an event
|
|
*/
|
|
public on<T>(event: ProxyEvents, handler: EventHandler<T>): void {
|
|
this.emitter.on(event, handler);
|
|
}
|
|
|
|
/**
|
|
* Subscribe to an event once
|
|
*/
|
|
public once<T>(event: ProxyEvents, handler: EventHandler<T>): void {
|
|
this.emitter.once(event, handler);
|
|
}
|
|
|
|
/**
|
|
* Unsubscribe from an event
|
|
*/
|
|
public off<T>(event: ProxyEvents, handler: EventHandler<T>): void {
|
|
this.emitter.off(event, handler);
|
|
}
|
|
|
|
/**
|
|
* Map Port80Handler events to standard proxy events
|
|
*/
|
|
public subscribePort80HandlerEvents(handler: any): void {
|
|
handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
|
|
this.emitCertificateIssued({
|
|
...data,
|
|
isRenewal: false,
|
|
source: 'port80handler'
|
|
});
|
|
});
|
|
|
|
handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
|
|
this.emitCertificateRenewed({
|
|
...data,
|
|
isRenewal: true,
|
|
source: 'port80handler'
|
|
});
|
|
});
|
|
|
|
handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, (data: ICertificateFailure) => {
|
|
this.emitCertificateFailed(data);
|
|
});
|
|
|
|
handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, (data: ICertificateExpiring) => {
|
|
this.emitCertificateExpiring(data);
|
|
});
|
|
}
|
|
} |