fix(typescript): Refactor types and interfaces to use consistent I prefix and update related tests

This commit is contained in:
Philipp Kunz 2025-05-09 22:46:53 +00:00
parent d924190680
commit f8647516b5
45 changed files with 403 additions and 392 deletions

View File

@ -1,5 +1,13 @@
# Changelog # Changelog
## 2025-05-09 - 13.1.1 - fix(typescript)
Refactor types and interfaces to use consistent 'I' prefix and update related tests
- Replaced DomainConfig with IDomainConfig and SmartProxyOptions with ISmartProxyOptions in various modules
- Renamed SmartProxyCertProvisionObject to TSmartProxyCertProvisionObject for clarity
- Standardized type names (e.g. ForwardConfig → IForwardConfig, Logger → ILogger) across proxy, forwarding, and certificate modules
- Updated tests and helper functions to reflect new type names and ensure compatibility
## 2025-05-09 - 13.1.0 - feat(docs) ## 2025-05-09 - 13.1.0 - feat(docs)
Update README to reflect new modular architecture and expanded core utilities: add Project Architecture Overview, update export paths and API references, and mark plan tasks as completed Update README to reflect new modular architecture and expanded core utilities: add Project Architecture Overview, update export paths and API references, and mark plan tasks as completed

View File

@ -1,9 +1,10 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js'; import * as plugins from '../ts/plugins.js';
import { CertProvisioner } from '../ts/certificate/providers/cert-provisioner.js'; import { CertProvisioner } from '../ts/certificate/providers/cert-provisioner.js';
import type { DomainConfig } from '../ts/forwarding/config/forwarding-types.js'; import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js';
import type { SmartProxyCertProvisionObject } from '../ts/certificate/models/certificate-types.js'; import type { ICertificateData } from '../ts/certificate/models/certificate-types.js';
import type { CertificateData } from '../ts/certificate/models/certificate-types.js'; // Import SmartProxyCertProvisionObject type alias
import type { TSmartProxyCertProvisionObject } from '../ts/certificate/providers/cert-provisioner.js';
// Fake Port80Handler stub // Fake Port80Handler stub
class FakePort80Handler extends plugins.EventEmitter { class FakePort80Handler extends plugins.EventEmitter {
@ -19,15 +20,15 @@ class FakePort80Handler extends plugins.EventEmitter {
// Fake NetworkProxyBridge stub // Fake NetworkProxyBridge stub
class FakeNetworkProxyBridge { class FakeNetworkProxyBridge {
public appliedCerts: CertificateData[] = []; public appliedCerts: ICertificateData[] = [];
applyExternalCertificate(cert: CertificateData) { applyExternalCertificate(cert: ICertificateData) {
this.appliedCerts.push(cert); this.appliedCerts.push(cert);
} }
} }
tap.test('CertProvisioner handles static provisioning', async () => { tap.test('CertProvisioner handles static provisioning', async () => {
const domain = 'static.com'; const domain = 'static.com';
const domainConfigs: DomainConfig[] = [{ const domainConfigs: IDomainConfig[] = [{
domains: [domain], domains: [domain],
forwarding: { forwarding: {
type: 'https-terminate-to-https', type: 'https-terminate-to-https',
@ -37,7 +38,7 @@ tap.test('CertProvisioner handles static provisioning', async () => {
const fakePort80 = new FakePort80Handler(); const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge(); const fakeBridge = new FakeNetworkProxyBridge();
// certProvider returns static certificate // certProvider returns static certificate
const certProvider = async (d: string): Promise<SmartProxyCertProvisionObject> => { const certProvider = async (d: string): Promise<TSmartProxyCertProvisionObject> => {
expect(d).toEqual(domain); expect(d).toEqual(domain);
return { return {
domainName: domain, domainName: domain,
@ -75,7 +76,7 @@ tap.test('CertProvisioner handles static provisioning', async () => {
tap.test('CertProvisioner handles http01 provisioning', async () => { tap.test('CertProvisioner handles http01 provisioning', async () => {
const domain = 'http01.com'; const domain = 'http01.com';
const domainConfigs: DomainConfig[] = [{ const domainConfigs: IDomainConfig[] = [{
domains: [domain], domains: [domain],
forwarding: { forwarding: {
type: 'https-terminate-to-http', type: 'https-terminate-to-http',
@ -85,7 +86,7 @@ tap.test('CertProvisioner handles http01 provisioning', async () => {
const fakePort80 = new FakePort80Handler(); const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge(); const fakeBridge = new FakeNetworkProxyBridge();
// certProvider returns http01 directive // certProvider returns http01 directive
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => 'http01'; const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => 'http01';
const prov = new CertProvisioner( const prov = new CertProvisioner(
domainConfigs, domainConfigs,
fakePort80 as any, fakePort80 as any,
@ -106,7 +107,7 @@ tap.test('CertProvisioner handles http01 provisioning', async () => {
tap.test('CertProvisioner on-demand http01 renewal', async () => { tap.test('CertProvisioner on-demand http01 renewal', async () => {
const domain = 'renew.com'; const domain = 'renew.com';
const domainConfigs: DomainConfig[] = [{ const domainConfigs: IDomainConfig[] = [{
domains: [domain], domains: [domain],
forwarding: { forwarding: {
type: 'https-terminate-to-http', type: 'https-terminate-to-http',
@ -115,7 +116,7 @@ tap.test('CertProvisioner on-demand http01 renewal', async () => {
}]; }];
const fakePort80 = new FakePort80Handler(); const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge(); const fakeBridge = new FakeNetworkProxyBridge();
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => 'http01'; const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => 'http01';
const prov = new CertProvisioner( const prov = new CertProvisioner(
domainConfigs, domainConfigs,
fakePort80 as any, fakePort80 as any,
@ -132,7 +133,7 @@ tap.test('CertProvisioner on-demand http01 renewal', async () => {
tap.test('CertProvisioner on-demand static provisioning', async () => { tap.test('CertProvisioner on-demand static provisioning', async () => {
const domain = 'ondemand.com'; const domain = 'ondemand.com';
const domainConfigs: DomainConfig[] = [{ const domainConfigs: IDomainConfig[] = [{
domains: [domain], domains: [domain],
forwarding: { forwarding: {
type: 'https-terminate-to-https', type: 'https-terminate-to-https',
@ -141,7 +142,7 @@ tap.test('CertProvisioner on-demand static provisioning', async () => {
}]; }];
const fakePort80 = new FakePort80Handler(); const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge(); const fakeBridge = new FakeNetworkProxyBridge();
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => ({ const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => ({
domainName: domain, domainName: domain,
publicKey: 'PKEY', publicKey: 'PKEY',
privateKey: 'PRIV', privateKey: 'PRIV',

View File

@ -2,8 +2,8 @@ import * as plugins from '../ts/plugins.js';
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { SmartProxy } from '../ts/proxies/smart-proxy/index.js'; import { SmartProxy } from '../ts/proxies/smart-proxy/index.js';
import type { DomainConfig } from '../ts/forwarding/config/forwarding-types.js'; import type { TForwardingType } from '../ts/forwarding/config/forwarding-types.js';
import type { ForwardingType } from '../ts/forwarding/config/forwarding-types.js'; import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js';
import { import {
httpOnly, httpOnly,
httpsPassthrough, httpsPassthrough,
@ -14,7 +14,7 @@ import {
// Test to demonstrate various forwarding configurations // Test to demonstrate various forwarding configurations
tap.test('Forwarding configuration examples', async (tools) => { tap.test('Forwarding configuration examples', async (tools) => {
// Example 1: HTTP-only configuration // Example 1: HTTP-only configuration
const httpOnlyConfig: DomainConfig = { const httpOnlyConfig: IDomainConfig = {
domains: ['http.example.com'], domains: ['http.example.com'],
forwarding: httpOnly({ forwarding: httpOnly({
target: { target: {
@ -30,7 +30,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(httpOnlyConfig.forwarding.type).toEqual('http-only'); expect(httpOnlyConfig.forwarding.type).toEqual('http-only');
// Example 2: HTTPS Passthrough (SNI) // Example 2: HTTPS Passthrough (SNI)
const httpsPassthroughConfig: DomainConfig = { const httpsPassthroughConfig: IDomainConfig = {
domains: ['pass.example.com'], domains: ['pass.example.com'],
forwarding: httpsPassthrough({ forwarding: httpsPassthrough({
target: { target: {
@ -47,7 +47,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(Array.isArray(httpsPassthroughConfig.forwarding.target.host)).toBeTrue(); expect(Array.isArray(httpsPassthroughConfig.forwarding.target.host)).toBeTrue();
// Example 3: HTTPS Termination to HTTP Backend // Example 3: HTTPS Termination to HTTP Backend
const terminateToHttpConfig: DomainConfig = { const terminateToHttpConfig: IDomainConfig = {
domains: ['secure.example.com'], domains: ['secure.example.com'],
forwarding: tlsTerminateToHttp({ forwarding: tlsTerminateToHttp({
target: { target: {
@ -75,7 +75,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(terminateToHttpConfig.forwarding.http?.redirectToHttps).toBeTrue(); expect(terminateToHttpConfig.forwarding.http?.redirectToHttps).toBeTrue();
// Example 4: HTTPS Termination to HTTPS Backend // Example 4: HTTPS Termination to HTTPS Backend
const terminateToHttpsConfig: DomainConfig = { const terminateToHttpsConfig: IDomainConfig = {
domains: ['proxy.example.com'], domains: ['proxy.example.com'],
forwarding: tlsTerminateToHttps({ forwarding: tlsTerminateToHttps({
target: { target: {

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js'; import * as plugins from '../ts/plugins.js';
import type { ForwardConfig, ForwardingType } from '../ts/forwarding/config/forwarding-types.js'; import type { IForwardConfig, TForwardingType } from '../ts/forwarding/config/forwarding-types.js';
// First, import the components directly to avoid issues with compiled modules // First, import the components directly to avoid issues with compiled modules
import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js'; import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js';
@ -17,7 +17,7 @@ const helpers = {
tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => { tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => {
// HTTP-only defaults // HTTP-only defaults
const httpConfig: ForwardConfig = { const httpConfig: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -26,7 +26,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedHttpConfig.http?.enabled).toEqual(true); expect(expandedHttpConfig.http?.enabled).toEqual(true);
// HTTPS-passthrough defaults // HTTPS-passthrough defaults
const passthroughConfig: ForwardConfig = { const passthroughConfig: IForwardConfig = {
type: 'https-passthrough', type: 'https-passthrough',
target: { host: 'localhost', port: 443 } target: { host: 'localhost', port: 443 }
}; };
@ -36,7 +36,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedPassthroughConfig.http?.enabled).toEqual(false); expect(expandedPassthroughConfig.http?.enabled).toEqual(false);
// HTTPS-terminate-to-http defaults // HTTPS-terminate-to-http defaults
const terminateToHttpConfig: ForwardConfig = { const terminateToHttpConfig: IForwardConfig = {
type: 'https-terminate-to-http', type: 'https-terminate-to-http',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -48,7 +48,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true); expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true);
// HTTPS-terminate-to-https defaults // HTTPS-terminate-to-https defaults
const terminateToHttpsConfig: ForwardConfig = { const terminateToHttpsConfig: IForwardConfig = {
type: 'https-terminate-to-https', type: 'https-terminate-to-https',
target: { host: 'localhost', port: 8443 } target: { host: 'localhost', port: 8443 }
}; };
@ -62,7 +62,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
tap.test('ForwardingHandlerFactory - validate configuration', async () => { tap.test('ForwardingHandlerFactory - validate configuration', async () => {
// Valid configuration // Valid configuration
const validConfig: ForwardConfig = { const validConfig: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -77,7 +77,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow(); expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow();
// Invalid configuration - invalid port // Invalid configuration - invalid port
const invalidConfig2: ForwardConfig = { const invalidConfig2: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 0 } target: { host: 'localhost', port: 0 }
}; };
@ -85,7 +85,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig2)).toThrow(); expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig2)).toThrow();
// Invalid configuration - HTTP disabled for HTTP-only // Invalid configuration - HTTP disabled for HTTP-only
const invalidConfig3: ForwardConfig = { const invalidConfig3: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 3000 }, target: { host: 'localhost', port: 3000 },
http: { enabled: false } http: { enabled: false }
@ -94,7 +94,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig3)).toThrow(); expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig3)).toThrow();
// Invalid configuration - HTTP enabled for HTTPS passthrough // Invalid configuration - HTTP enabled for HTTPS passthrough
const invalidConfig4: ForwardConfig = { const invalidConfig4: IForwardConfig = {
type: 'https-passthrough', type: 'https-passthrough',
target: { host: 'localhost', port: 443 }, target: { host: 'localhost', port: 443 },
http: { enabled: true } http: { enabled: true }

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js'; import * as plugins from '../ts/plugins.js';
import type { ForwardConfig } from '../ts/forwarding/config/forwarding-types.js'; import type { IForwardConfig } from '../ts/forwarding/config/forwarding-types.js';
// First, import the components directly to avoid issues with compiled modules // First, import the components directly to avoid issues with compiled modules
import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js'; import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js';
@ -17,7 +17,7 @@ const helpers = {
tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => { tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => {
// HTTP-only defaults // HTTP-only defaults
const httpConfig: ForwardConfig = { const httpConfig: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -26,7 +26,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedHttpConfig.http?.enabled).toEqual(true); expect(expandedHttpConfig.http?.enabled).toEqual(true);
// HTTPS-passthrough defaults // HTTPS-passthrough defaults
const passthroughConfig: ForwardConfig = { const passthroughConfig: IForwardConfig = {
type: 'https-passthrough', type: 'https-passthrough',
target: { host: 'localhost', port: 443 } target: { host: 'localhost', port: 443 }
}; };
@ -36,7 +36,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedPassthroughConfig.http?.enabled).toEqual(false); expect(expandedPassthroughConfig.http?.enabled).toEqual(false);
// HTTPS-terminate-to-http defaults // HTTPS-terminate-to-http defaults
const terminateToHttpConfig: ForwardConfig = { const terminateToHttpConfig: IForwardConfig = {
type: 'https-terminate-to-http', type: 'https-terminate-to-http',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -48,7 +48,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true); expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true);
// HTTPS-terminate-to-https defaults // HTTPS-terminate-to-https defaults
const terminateToHttpsConfig: ForwardConfig = { const terminateToHttpsConfig: IForwardConfig = {
type: 'https-terminate-to-https', type: 'https-terminate-to-https',
target: { host: 'localhost', port: 8443 } target: { host: 'localhost', port: 8443 }
}; };
@ -62,7 +62,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
tap.test('ForwardingHandlerFactory - validate configuration', async () => { tap.test('ForwardingHandlerFactory - validate configuration', async () => {
// Valid configuration // Valid configuration
const validConfig: ForwardConfig = { const validConfig: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 3000 } target: { host: 'localhost', port: 3000 }
}; };
@ -77,7 +77,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow(); expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow();
// Invalid configuration - invalid port // Invalid configuration - invalid port
const invalidConfig2: ForwardConfig = { const invalidConfig2: IForwardConfig = {
type: 'http-only', type: 'http-only',
target: { host: 'localhost', port: 0 } target: { host: 'localhost', port: 0 }
}; };

View File

@ -282,10 +282,20 @@ tap.test('should support optional source IP preservation in chained proxies', as
// Test round-robin behavior for multiple target hosts in a domain config. // Test round-robin behavior for multiple target hosts in a domain config.
tap.test('should use round robin for multiple target hosts in domain config', async () => { tap.test('should use round robin for multiple target hosts in domain config', async () => {
// Create a domain config with multiple hosts in the target // Create a domain config with multiple hosts in the target
const domainConfig = { const domainConfig: {
domains: string[];
forwarding: {
type: 'http-only';
target: {
host: string[];
port: number;
};
http: { enabled: boolean };
}
} = {
domains: ['rr.test'], domains: ['rr.test'],
forwarding: { forwarding: {
type: 'http-only', type: 'http-only' as const,
target: { target: {
host: ['hostA', 'hostB'], // Array of hosts for round-robin host: ['hostA', 'hostB'], // Array of hosts for round-robin
port: 80 port: 80

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '13.1.0', version: '13.1.1',
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.' description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
} }

View File

@ -1,6 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import type { AcmeOptions } from '../models/certificate-types.js'; import type { IAcmeOptions } from '../models/certificate-types.js';
import { ensureCertificateDirectory } from '../utils/certificate-helpers.js'; import { ensureCertificateDirectory } from '../utils/certificate-helpers.js';
// We'll need to update this import when we move the Port80Handler // We'll need to update this import when we move the Port80Handler
import { Port80Handler } from '../../http/port80/port80-handler.js'; import { Port80Handler } from '../../http/port80/port80-handler.js';
@ -12,7 +12,7 @@ import { Port80Handler } from '../../http/port80/port80-handler.js';
* @returns A new Port80Handler instance * @returns A new Port80Handler instance
*/ */
export function buildPort80Handler( export function buildPort80Handler(
options: AcmeOptions options: IAcmeOptions
): Port80Handler { ): Port80Handler {
if (options.certificateStore) { if (options.certificateStore) {
ensureCertificateDirectory(options.certificateStore); ensureCertificateDirectory(options.certificateStore);
@ -32,7 +32,7 @@ export function createDefaultAcmeOptions(
email: string, email: string,
certificateStore: string, certificateStore: string,
useProduction: boolean = false useProduction: boolean = false
): AcmeOptions { ): IAcmeOptions {
return { return {
accountEmail: email, accountEmail: email,
enabled: true, enabled: true,

View File

@ -1,12 +1,12 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { AcmeOptions, CertificateData } from '../models/certificate-types.js'; import type { IAcmeOptions, ICertificateData } from '../models/certificate-types.js';
import { CertificateEvents } from '../events/certificate-events.js'; import { CertificateEvents } from '../events/certificate-events.js';
/** /**
* Manages ACME challenges and certificate validation * Manages ACME challenges and certificate validation
*/ */
export class AcmeChallengeHandler extends plugins.EventEmitter { export class AcmeChallengeHandler extends plugins.EventEmitter {
private options: AcmeOptions; private options: IAcmeOptions;
private client: any; // ACME client from plugins private client: any; // ACME client from plugins
private pendingChallenges: Map<string, any>; private pendingChallenges: Map<string, any>;
@ -14,7 +14,7 @@ export class AcmeChallengeHandler extends plugins.EventEmitter {
* Creates a new ACME challenge handler * Creates a new ACME challenge handler
* @param options ACME configuration options * @param options ACME configuration options
*/ */
constructor(options: AcmeOptions) { constructor(options: IAcmeOptions) {
super(); super();
this.options = options; this.options = options;
this.pendingChallenges = new Map(); this.pendingChallenges = new Map();

View File

@ -25,8 +25,8 @@ export * from './storage/file-storage.js';
// Convenience function to create a certificate provisioner with common settings // Convenience function to create a certificate provisioner with common settings
import { CertProvisioner } from './providers/cert-provisioner.js'; import { CertProvisioner } from './providers/cert-provisioner.js';
import { buildPort80Handler } from './acme/acme-factory.js'; import { buildPort80Handler } from './acme/acme-factory.js';
import type { AcmeOptions, DomainForwardConfig } from './models/certificate-types.js'; import type { IAcmeOptions, IDomainForwardConfig } from './models/certificate-types.js';
import type { DomainConfig } from '../forwarding/config/domain-config.js'; import type { IDomainConfig } from '../forwarding/config/domain-config.js';
/** /**
* Creates a complete certificate provisioning system with default settings * Creates a complete certificate provisioning system with default settings
@ -37,8 +37,8 @@ import type { DomainConfig } from '../forwarding/config/domain-config.js';
* @returns Configured CertProvisioner * @returns Configured CertProvisioner
*/ */
export function createCertificateProvisioner( export function createCertificateProvisioner(
domainConfigs: DomainConfig[], domainConfigs: IDomainConfig[],
acmeOptions: AcmeOptions, acmeOptions: IAcmeOptions,
networkProxyBridge: any, // Placeholder until NetworkProxyBridge is migrated networkProxyBridge: any, // Placeholder until NetworkProxyBridge is migrated
certProvider?: any // Placeholder until cert provider type is properly defined certProvider?: any // Placeholder until cert provider type is properly defined
): CertProvisioner { ): CertProvisioner {

View File

@ -85,3 +85,4 @@ export interface IAcmeOptions {
skipConfiguredCerts?: boolean; // Skip domains with existing certificates skipConfiguredCerts?: boolean; // Skip domains with existing certificates
domainForwards?: IDomainForwardConfig[]; // Domain-specific forwarding configs domainForwards?: IDomainForwardConfig[]; // Domain-specific forwarding configs
} }

View File

@ -1,34 +1,34 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { DomainConfig } from '../../forwarding/config/domain-config.js'; import type { IDomainConfig } from '../../forwarding/config/domain-config.js';
import type { CertificateData, DomainForwardConfig, DomainOptions } from '../models/certificate-types.js'; import type { ICertificateData, IDomainForwardConfig, IDomainOptions } from '../models/certificate-types.js';
import { Port80HandlerEvents, CertProvisionerEvents } from '../events/certificate-events.js'; import { Port80HandlerEvents, CertProvisionerEvents } from '../events/certificate-events.js';
import { Port80Handler } from '../../http/port80/port80-handler.js'; import { Port80Handler } from '../../http/port80/port80-handler.js';
// We need to define this interface until we migrate NetworkProxyBridge // We need to define this interface until we migrate NetworkProxyBridge
interface NetworkProxyBridge { interface INetworkProxyBridge {
applyExternalCertificate(certData: CertificateData): void; applyExternalCertificate(certData: ICertificateData): void;
} }
// This will be imported after NetworkProxyBridge is migrated // This will be imported after NetworkProxyBridge is migrated
// import type { NetworkProxyBridge } from '../../proxies/smart-proxy/network-proxy-bridge.js'; // import type { NetworkProxyBridge } from '../../proxies/smart-proxy/network-proxy-bridge.js';
// For backward compatibility // For backward compatibility
export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01'; export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
/** /**
* Type for static certificate provisioning * Type for static certificate provisioning
*/ */
export type CertProvisionObject = plugins.tsclass.network.ICert | 'http01' | 'dns01'; export type TCertProvisionObject = plugins.tsclass.network.ICert | 'http01' | 'dns01';
/** /**
* CertProvisioner manages certificate provisioning and renewal workflows, * CertProvisioner manages certificate provisioning and renewal workflows,
* unifying static certificates and HTTP-01 challenges via Port80Handler. * unifying static certificates and HTTP-01 challenges via Port80Handler.
*/ */
export class CertProvisioner extends plugins.EventEmitter { export class CertProvisioner extends plugins.EventEmitter {
private domainConfigs: DomainConfig[]; private domainConfigs: IDomainConfig[];
private port80Handler: Port80Handler; private port80Handler: Port80Handler;
private networkProxyBridge: NetworkProxyBridge; private networkProxyBridge: INetworkProxyBridge;
private certProvisionFunction?: (domain: string) => Promise<CertProvisionObject>; private certProvisionFunction?: (domain: string) => Promise<TCertProvisionObject>;
private forwardConfigs: DomainForwardConfig[]; private forwardConfigs: IDomainForwardConfig[];
private renewThresholdDays: number; private renewThresholdDays: number;
private renewCheckIntervalHours: number; private renewCheckIntervalHours: number;
private autoRenew: boolean; private autoRenew: boolean;
@ -47,14 +47,14 @@ export class CertProvisioner extends plugins.EventEmitter {
* @param forwardConfigs Domain forwarding configurations for ACME challenges * @param forwardConfigs Domain forwarding configurations for ACME challenges
*/ */
constructor( constructor(
domainConfigs: DomainConfig[], domainConfigs: IDomainConfig[],
port80Handler: Port80Handler, port80Handler: Port80Handler,
networkProxyBridge: NetworkProxyBridge, networkProxyBridge: INetworkProxyBridge,
certProvider?: (domain: string) => Promise<CertProvisionObject>, certProvider?: (domain: string) => Promise<TCertProvisionObject>,
renewThresholdDays: number = 30, renewThresholdDays: number = 30,
renewCheckIntervalHours: number = 24, renewCheckIntervalHours: number = 24,
autoRenew: boolean = true, autoRenew: boolean = true,
forwardConfigs: DomainForwardConfig[] = [] forwardConfigs: IDomainForwardConfig[] = []
) { ) {
super(); super();
this.domainConfigs = domainConfigs; this.domainConfigs = domainConfigs;
@ -92,11 +92,11 @@ export class CertProvisioner extends plugins.EventEmitter {
*/ */
private setupEventSubscriptions(): void { private setupEventSubscriptions(): void {
// We need to reimplement subscribeToPort80Handler here // We need to reimplement subscribeToPort80Handler here
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: CertificateData) => { this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, { ...data, source: 'http01', isRenewal: false }); this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, { ...data, source: 'http01', isRenewal: false });
}); });
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: CertificateData) => { this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
this.emit(CertProvisionerEvents.CERTIFICATE_RENEWED, { ...data, source: 'http01', isRenewal: true }); this.emit(CertProvisionerEvents.CERTIFICATE_RENEWED, { ...data, source: 'http01', isRenewal: true });
}); });
@ -110,7 +110,7 @@ export class CertProvisioner extends plugins.EventEmitter {
*/ */
private setupForwardingConfigs(): void { private setupForwardingConfigs(): void {
for (const config of this.forwardConfigs) { for (const config of this.forwardConfigs) {
const domainOptions: DomainOptions = { const domainOptions: IDomainOptions = {
domainName: config.domain, domainName: config.domain,
sslRedirect: config.sslRedirect || false, sslRedirect: config.sslRedirect || false,
acmeMaintenance: false, acmeMaintenance: false,
@ -138,7 +138,7 @@ export class CertProvisioner extends plugins.EventEmitter {
*/ */
private async provisionDomain(domain: string): Promise<void> { private async provisionDomain(domain: string): Promise<void> {
const isWildcard = domain.includes('*'); const isWildcard = domain.includes('*');
let provision: CertProvisionObject = 'http01'; let provision: TCertProvisionObject = 'http01';
// Try to get a certificate from the provision function // Try to get a certificate from the provision function
if (this.certProvisionFunction) { if (this.certProvisionFunction) {
@ -174,7 +174,7 @@ export class CertProvisioner extends plugins.EventEmitter {
// Static certificate (e.g., DNS-01 provisioned or user-provided) // Static certificate (e.g., DNS-01 provisioned or user-provided)
this.provisionMap.set(domain, 'static'); this.provisionMap.set(domain, 'static');
const certObj = provision as plugins.tsclass.network.ICert; const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = { const certData: ICertificateData = {
domain: certObj.domainName, domain: certObj.domainName,
certificate: certObj.publicKey, certificate: certObj.publicKey,
privateKey: certObj.privateKey, privateKey: certObj.privateKey,
@ -235,7 +235,7 @@ export class CertProvisioner extends plugins.EventEmitter {
if (provision !== 'http01' && provision !== 'dns01') { if (provision !== 'http01' && provision !== 'dns01') {
const certObj = provision as plugins.tsclass.network.ICert; const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = { const certData: ICertificateData = {
domain: certObj.domainName, domain: certObj.domainName,
certificate: certObj.publicKey, certificate: certObj.publicKey,
privateKey: certObj.privateKey, privateKey: certObj.privateKey,
@ -267,7 +267,7 @@ export class CertProvisioner extends plugins.EventEmitter {
const isWildcard = domain.includes('*'); const isWildcard = domain.includes('*');
// Determine provisioning method // Determine provisioning method
let provision: CertProvisionObject = 'http01'; let provision: TCertProvisionObject = 'http01';
if (this.certProvisionFunction) { if (this.certProvisionFunction) {
provision = await this.certProvisionFunction(domain); provision = await this.certProvisionFunction(domain);
@ -288,7 +288,7 @@ export class CertProvisioner extends plugins.EventEmitter {
} else { } else {
// Static certificate (e.g., DNS-01 provisioned) supports wildcards // Static certificate (e.g., DNS-01 provisioned) supports wildcards
const certObj = provision as plugins.tsclass.network.ICert; const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = { const certData: ICertificateData = {
domain: certObj.domainName, domain: certObj.domainName,
certificate: certObj.publicKey, certificate: certObj.publicKey,
privateKey: certObj.privateKey, privateKey: certObj.privateKey,
@ -311,7 +311,7 @@ export class CertProvisioner extends plugins.EventEmitter {
sslRedirect?: boolean; sslRedirect?: boolean;
acmeMaintenance?: boolean; acmeMaintenance?: boolean;
}): Promise<void> { }): Promise<void> {
const domainOptions: DomainOptions = { const domainOptions: IDomainOptions = {
domainName: domain, domainName: domain,
sslRedirect: options?.sslRedirect || true, sslRedirect: options?.sslRedirect || true,
acmeMaintenance: options?.acmeMaintenance || true acmeMaintenance: options?.acmeMaintenance || true

View File

@ -1,7 +1,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { CertificateData, Certificates } from '../models/certificate-types.js'; import type { ICertificateData, ICertificates } from '../models/certificate-types.js';
import { ensureCertificateDirectory } from '../utils/certificate-helpers.js'; import { ensureCertificateDirectory } from '../utils/certificate-helpers.js';
/** /**
@ -24,7 +24,7 @@ export class FileStorage {
* @param domain Domain name * @param domain Domain name
* @param certData Certificate data to save * @param certData Certificate data to save
*/ */
public async saveCertificate(domain: string, certData: CertificateData): Promise<void> { public async saveCertificate(domain: string, certData: ICertificateData): Promise<void> {
const sanitizedDomain = this.sanitizeDomain(domain); const sanitizedDomain = this.sanitizeDomain(domain);
const certDir = path.join(this.storageDir, sanitizedDomain); const certDir = path.join(this.storageDir, sanitizedDomain);
ensureCertificateDirectory(certDir); ensureCertificateDirectory(certDir);
@ -57,7 +57,7 @@ export class FileStorage {
* @param domain Domain name * @param domain Domain name
* @returns Certificate data if found, null otherwise * @returns Certificate data if found, null otherwise
*/ */
public async loadCertificate(domain: string): Promise<CertificateData | null> { public async loadCertificate(domain: string): Promise<ICertificateData | null> {
const sanitizedDomain = this.sanitizeDomain(domain); const sanitizedDomain = this.sanitizeDomain(domain);
const certDir = path.join(this.storageDir, sanitizedDomain); const certDir = path.join(this.storageDir, sanitizedDomain);

View File

@ -1,7 +1,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import type { Certificates } from '../models/certificate-types.js'; import type { ICertificates } from '../models/certificate-types.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url)); const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -9,7 +9,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
* Loads the default SSL certificates from the assets directory * Loads the default SSL certificates from the assets directory
* @returns The certificate key pair * @returns The certificate key pair
*/ */
export function loadDefaultCertificates(): Certificates { export function loadDefaultCertificates(): ICertificates {
try { try {
// Need to adjust path from /ts/certificate/utils to /assets/certs // Need to adjust path from /ts/certificate/utils to /assets/certs
const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs'); const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');

View File

@ -6,7 +6,7 @@ import type {
} from './types.js'; } from './types.js';
import type { import type {
ForwardConfig as IForwardConfig IForwardConfig
} from '../forwarding/config/forwarding-types.js'; } from '../forwarding/config/forwarding-types.js';
/** /**

View File

@ -1,14 +1,14 @@
import type { ForwardConfig } from './forwarding-types.js'; import type { IForwardConfig } from './forwarding-types.js';
/** /**
* Domain configuration with unified forwarding configuration * Domain configuration with unified forwarding configuration
*/ */
export interface DomainConfig { export interface IDomainConfig {
// Core properties - domain patterns // Core properties - domain patterns
domains: string[]; domains: string[];
// Unified forwarding configuration // Unified forwarding configuration
forwarding: ForwardConfig; forwarding: IForwardConfig;
} }
/** /**
@ -16,8 +16,8 @@ export interface DomainConfig {
*/ */
export function createDomainConfig( export function createDomainConfig(
domains: string | string[], domains: string | string[],
forwarding: ForwardConfig forwarding: IForwardConfig
): DomainConfig { ): IDomainConfig {
// Normalize domains to an array // Normalize domains to an array
const domainArray = Array.isArray(domains) ? domains : [domains]; const domainArray = Array.isArray(domains) ? domains : [domains];
@ -26,6 +26,3 @@ export function createDomainConfig(
forwarding forwarding
}; };
} }
// Backwards compatibility
export interface IDomainConfig extends DomainConfig {}

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { DomainConfig } from './domain-config.js'; import type { IDomainConfig } from './domain-config.js';
import { ForwardingHandler } from '../handlers/base-handler.js'; import { ForwardingHandler } from '../handlers/base-handler.js';
import { ForwardingHandlerEvents } from './forwarding-types.js'; import { ForwardingHandlerEvents } from './forwarding-types.js';
import { ForwardingHandlerFactory } from '../factory/forwarding-factory.js'; import { ForwardingHandlerFactory } from '../factory/forwarding-factory.js';
@ -21,14 +21,14 @@ export enum DomainManagerEvents {
* Manages domains and their forwarding handlers * Manages domains and their forwarding handlers
*/ */
export class DomainManager extends plugins.EventEmitter { export class DomainManager extends plugins.EventEmitter {
private domainConfigs: DomainConfig[] = []; private domainConfigs: IDomainConfig[] = [];
private domainHandlers: Map<string, ForwardingHandler> = new Map(); private domainHandlers: Map<string, ForwardingHandler> = new Map();
/** /**
* Create a new DomainManager * Create a new DomainManager
* @param initialDomains Optional initial domain configurations * @param initialDomains Optional initial domain configurations
*/ */
constructor(initialDomains?: DomainConfig[]) { constructor(initialDomains?: IDomainConfig[]) {
super(); super();
if (initialDomains) { if (initialDomains) {
@ -40,7 +40,7 @@ export class DomainManager extends plugins.EventEmitter {
* Set or replace all domain configurations * Set or replace all domain configurations
* @param configs Array of domain configurations * @param configs Array of domain configurations
*/ */
public async setDomainConfigs(configs: DomainConfig[]): Promise<void> { public async setDomainConfigs(configs: IDomainConfig[]): Promise<void> {
// Clear existing handlers // Clear existing handlers
this.domainHandlers.clear(); this.domainHandlers.clear();
@ -57,7 +57,7 @@ export class DomainManager extends plugins.EventEmitter {
* Add a new domain configuration * Add a new domain configuration
* @param config The domain configuration to add * @param config The domain configuration to add
*/ */
public async addDomainConfig(config: DomainConfig): Promise<void> { public async addDomainConfig(config: IDomainConfig): Promise<void> {
// Check if any of these domains already exist // Check if any of these domains already exist
for (const domain of config.domains) { for (const domain of config.domains) {
if (this.domainHandlers.has(domain)) { if (this.domainHandlers.has(domain)) {
@ -193,7 +193,7 @@ export class DomainManager extends plugins.EventEmitter {
* Create handlers for a domain configuration * Create handlers for a domain configuration
* @param config The domain configuration * @param config The domain configuration
*/ */
private async createHandlersForDomain(config: DomainConfig): Promise<void> { private async createHandlersForDomain(config: IDomainConfig): Promise<void> {
try { try {
// Create a handler for this forwarding configuration // Create a handler for this forwarding configuration
const handler = ForwardingHandlerFactory.createHandler(config.forwarding); const handler = ForwardingHandlerFactory.createHandler(config.forwarding);
@ -221,7 +221,7 @@ export class DomainManager extends plugins.EventEmitter {
* @param handler The handler * @param handler The handler
* @param config The domain configuration for this handler * @param config The domain configuration for this handler
*/ */
private setupHandlerEvents(handler: ForwardingHandler, config: DomainConfig): void { private setupHandlerEvents(handler: ForwardingHandler, config: IDomainConfig): void {
// Forward relevant events // Forward relevant events
handler.on(ForwardingHandlerEvents.CERTIFICATE_NEEDED, (data) => { handler.on(ForwardingHandlerEvents.CERTIFICATE_NEEDED, (data) => {
this.emit(DomainManagerEvents.CERTIFICATE_NEEDED, { this.emit(DomainManagerEvents.CERTIFICATE_NEEDED, {
@ -277,7 +277,7 @@ export class DomainManager extends plugins.EventEmitter {
* Get all domain configurations * Get all domain configurations
* @returns Array of domain configurations * @returns Array of domain configurations
*/ */
public getDomainConfigs(): DomainConfig[] { public getDomainConfigs(): IDomainConfig[] {
return [...this.domainConfigs]; return [...this.domainConfigs];
} }
} }

View File

@ -3,7 +3,7 @@ import type * as plugins from '../../plugins.js';
/** /**
* The primary forwarding types supported by SmartProxy * The primary forwarding types supported by SmartProxy
*/ */
export type ForwardingType = export type TForwardingType =
| 'http-only' // HTTP forwarding only (no HTTPS) | 'http-only' // HTTP forwarding only (no HTTPS)
| 'https-passthrough' // Pass-through TLS traffic (SNI forwarding) | 'https-passthrough' // Pass-through TLS traffic (SNI forwarding)
| 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend | 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend
@ -12,7 +12,7 @@ export type ForwardingType =
/** /**
* Target configuration for forwarding * Target configuration for forwarding
*/ */
export interface TargetConfig { export interface ITargetConfig {
host: string | string[]; // Support single host or round-robin host: string | string[]; // Support single host or round-robin
port: number; port: number;
} }
@ -20,7 +20,7 @@ export interface TargetConfig {
/** /**
* HTTP-specific options for forwarding * HTTP-specific options for forwarding
*/ */
export interface HttpOptions { export interface IHttpOptions {
enabled?: boolean; // Whether HTTP is enabled enabled?: boolean; // Whether HTTP is enabled
redirectToHttps?: boolean; // Redirect HTTP to HTTPS redirectToHttps?: boolean; // Redirect HTTP to HTTPS
headers?: Record<string, string>; // Custom headers for HTTP responses headers?: Record<string, string>; // Custom headers for HTTP responses
@ -29,7 +29,7 @@ export interface HttpOptions {
/** /**
* HTTPS-specific options for forwarding * HTTPS-specific options for forwarding
*/ */
export interface HttpsOptions { export interface IHttpsOptions {
customCert?: { // Use custom cert instead of auto-provisioned customCert?: { // Use custom cert instead of auto-provisioned
key: string; key: string;
cert: string; cert: string;
@ -40,7 +40,7 @@ export interface HttpsOptions {
/** /**
* ACME certificate handling options * ACME certificate handling options
*/ */
export interface AcmeForwardingOptions { export interface IAcmeForwardingOptions {
enabled?: boolean; // Enable ACME certificate provisioning enabled?: boolean; // Enable ACME certificate provisioning
maintenance?: boolean; // Auto-renew certificates maintenance?: boolean; // Auto-renew certificates
production?: boolean; // Use production ACME servers production?: boolean; // Use production ACME servers
@ -54,7 +54,7 @@ export interface AcmeForwardingOptions {
/** /**
* Security options for forwarding * Security options for forwarding
*/ */
export interface SecurityOptions { export interface ISecurityOptions {
allowedIps?: string[]; // IPs allowed to connect allowedIps?: string[]; // IPs allowed to connect
blockedIps?: string[]; // IPs blocked from connecting blockedIps?: string[]; // IPs blocked from connecting
maxConnections?: number; // Max simultaneous connections maxConnections?: number; // Max simultaneous connections
@ -63,7 +63,7 @@ export interface SecurityOptions {
/** /**
* Advanced options for forwarding * Advanced options for forwarding
*/ */
export interface AdvancedOptions { export interface IAdvancedOptions {
portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges
networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode
keepAlive?: boolean; // Enable TCP keepalive keepAlive?: boolean; // Enable TCP keepalive
@ -74,21 +74,21 @@ export interface AdvancedOptions {
/** /**
* Unified forwarding configuration interface * Unified forwarding configuration interface
*/ */
export interface ForwardConfig { export interface IForwardConfig {
// Define the primary forwarding type - use-case driven approach // Define the primary forwarding type - use-case driven approach
type: ForwardingType; type: TForwardingType;
// Target configuration // Target configuration
target: TargetConfig; target: ITargetConfig;
// Protocol options // Protocol options
http?: HttpOptions; http?: IHttpOptions;
https?: HttpsOptions; https?: IHttpsOptions;
acme?: AcmeForwardingOptions; acme?: IAcmeForwardingOptions;
// Security and advanced options // Security and advanced options
security?: SecurityOptions; security?: ISecurityOptions;
advanced?: AdvancedOptions; advanced?: IAdvancedOptions;
} }
/** /**
@ -118,8 +118,8 @@ export interface IForwardingHandler extends plugins.EventEmitter {
* Helper function types for common forwarding patterns * Helper function types for common forwarding patterns
*/ */
export const httpOnly = ( export const httpOnly = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'> partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): ForwardConfig => ({ ): IForwardConfig => ({
type: 'http-only', type: 'http-only',
target: partialConfig.target, target: partialConfig.target,
http: { enabled: true, ...(partialConfig.http || {}) }, http: { enabled: true, ...(partialConfig.http || {}) },
@ -128,8 +128,8 @@ export const httpOnly = (
}); });
export const tlsTerminateToHttp = ( export const tlsTerminateToHttp = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'> partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): ForwardConfig => ({ ): IForwardConfig => ({
type: 'https-terminate-to-http', type: 'https-terminate-to-http',
target: partialConfig.target, target: partialConfig.target,
https: { ...(partialConfig.https || {}) }, https: { ...(partialConfig.https || {}) },
@ -140,8 +140,8 @@ export const tlsTerminateToHttp = (
}); });
export const tlsTerminateToHttps = ( export const tlsTerminateToHttps = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'> partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): ForwardConfig => ({ ): IForwardConfig => ({
type: 'https-terminate-to-https', type: 'https-terminate-to-https',
target: partialConfig.target, target: partialConfig.target,
https: { ...(partialConfig.https || {}) }, https: { ...(partialConfig.https || {}) },
@ -152,20 +152,11 @@ export const tlsTerminateToHttps = (
}); });
export const httpsPassthrough = ( export const httpsPassthrough = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'> partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): ForwardConfig => ({ ): IForwardConfig => ({
type: 'https-passthrough', type: 'https-passthrough',
target: partialConfig.target, target: partialConfig.target,
https: { forwardSni: true, ...(partialConfig.https || {}) }, https: { forwardSni: true, ...(partialConfig.https || {}) },
...(partialConfig.security ? { security: partialConfig.security } : {}), ...(partialConfig.security ? { security: partialConfig.security } : {}),
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {}) ...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
}); });
// Backwards compatibility interfaces with 'I' prefix
export interface ITargetConfig extends TargetConfig {}
export interface IHttpOptions extends HttpOptions {}
export interface IHttpsOptions extends HttpsOptions {}
export interface IAcmeForwardingOptions extends AcmeForwardingOptions {}
export interface ISecurityOptions extends SecurityOptions {}
export interface IAdvancedOptions extends AdvancedOptions {}
export interface IForwardConfig extends ForwardConfig {}

View File

@ -1,5 +1,5 @@
import type { ForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import type { ForwardingHandler } from '../handlers/base-handler.js'; import { ForwardingHandler } from '../handlers/base-handler.js';
import { HttpForwardingHandler } from '../handlers/http-handler.js'; import { HttpForwardingHandler } from '../handlers/http-handler.js';
import { HttpsPassthroughHandler } from '../handlers/https-passthrough-handler.js'; import { HttpsPassthroughHandler } from '../handlers/https-passthrough-handler.js';
import { HttpsTerminateToHttpHandler } from '../handlers/https-terminate-to-http-handler.js'; import { HttpsTerminateToHttpHandler } from '../handlers/https-terminate-to-http-handler.js';
@ -14,7 +14,7 @@ export class ForwardingHandlerFactory {
* @param config The forwarding configuration * @param config The forwarding configuration
* @returns The appropriate forwarding handler * @returns The appropriate forwarding handler
*/ */
public static createHandler(config: ForwardConfig): ForwardingHandler { public static createHandler(config: IForwardConfig): ForwardingHandler {
// Create the appropriate handler based on the forwarding type // Create the appropriate handler based on the forwarding type
switch (config.type) { switch (config.type) {
case 'http-only': case 'http-only':
@ -40,9 +40,9 @@ export class ForwardingHandlerFactory {
* @param config The original forwarding configuration * @param config The original forwarding configuration
* @returns A configuration with defaults applied * @returns A configuration with defaults applied
*/ */
public static applyDefaults(config: ForwardConfig): ForwardConfig { public static applyDefaults(config: IForwardConfig): IForwardConfig {
// Create a deep copy of the configuration // Create a deep copy of the configuration
const result: ForwardConfig = JSON.parse(JSON.stringify(config)); const result: IForwardConfig = JSON.parse(JSON.stringify(config));
// Apply defaults based on forwarding type // Apply defaults based on forwarding type
switch (config.type) { switch (config.type) {
@ -112,7 +112,7 @@ export class ForwardingHandlerFactory {
* @param config The configuration to validate * @param config The configuration to validate
* @throws Error if the configuration is invalid * @throws Error if the configuration is invalid
*/ */
public static validateConfig(config: ForwardConfig): void { public static validateConfig(config: IForwardConfig): void {
// Validate common properties // Validate common properties
if (!config.target) { if (!config.target) {
throw new Error('Forwarding configuration must include a target'); throw new Error('Forwarding configuration must include a target');

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { import type {
ForwardConfig, IForwardConfig,
IForwardingHandler IForwardingHandler
} from '../config/forwarding-types.js'; } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
@ -13,7 +13,7 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements
* Create a new ForwardingHandler * Create a new ForwardingHandler
* @param config The forwarding configuration * @param config The forwarding configuration
*/ */
constructor(protected config: ForwardConfig) { constructor(protected config: IForwardConfig) {
super(); super();
} }

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/** /**
@ -11,7 +11,7 @@ export class HttpForwardingHandler extends ForwardingHandler {
* Create a new HTTP forwarding handler * Create a new HTTP forwarding handler
* @param config The forwarding configuration * @param config The forwarding configuration
*/ */
constructor(config: ForwardConfig) { constructor(config: IForwardConfig) {
super(config); super(config);
// Validate that this is an HTTP-only configuration // Validate that this is an HTTP-only configuration
@ -20,6 +20,15 @@ export class HttpForwardingHandler extends ForwardingHandler {
} }
} }
/**
* Initialize the handler
* HTTP handler doesn't need special initialization
*/
public async initialize(): Promise<void> {
// Basic initialization from parent class
await super.initialize();
}
/** /**
* Handle a raw socket connection * Handle a raw socket connection
* HTTP handler doesn't do much with raw sockets as it mainly processes * HTTP handler doesn't do much with raw sockets as it mainly processes

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/** /**
@ -11,7 +11,7 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
* Create a new HTTPS passthrough handler * Create a new HTTPS passthrough handler
* @param config The forwarding configuration * @param config The forwarding configuration
*/ */
constructor(config: ForwardConfig) { constructor(config: IForwardConfig) {
super(config); super(config);
// Validate that this is an HTTPS passthrough configuration // Validate that this is an HTTPS passthrough configuration
@ -20,6 +20,15 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
} }
} }
/**
* Initialize the handler
* HTTPS passthrough handler doesn't need special initialization
*/
public async initialize(): Promise<void> {
// Basic initialization from parent class
await super.initialize();
}
/** /**
* Handle a TLS/SSL socket connection by forwarding it without termination * Handle a TLS/SSL socket connection by forwarding it without termination
* @param clientSocket The incoming socket from the client * @param clientSocket The incoming socket from the client

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/** /**
@ -14,7 +14,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
* Create a new HTTPS termination with HTTP backend handler * Create a new HTTPS termination with HTTP backend handler
* @param config The forwarding configuration * @param config The forwarding configuration
*/ */
constructor(config: ForwardConfig) { constructor(config: IForwardConfig) {
super(config); super(config);
// Validate that this is an HTTPS terminate to HTTP configuration // Validate that this is an HTTPS terminate to HTTP configuration

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/** /**
@ -13,7 +13,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
* Create a new HTTPS termination with HTTPS backend handler * Create a new HTTPS termination with HTTPS backend handler
* @param config The forwarding configuration * @param config The forwarding configuration
*/ */
constructor(config: ForwardConfig) { constructor(config: IForwardConfig) {
super(config); super(config);
// Validate that this is an HTTPS terminate to HTTPS configuration // Validate that this is an HTTPS terminate to HTTPS configuration

View File

@ -1,8 +1,8 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { import type {
ForwardConfig, IForwardConfig,
DomainOptions, IDomainOptions,
AcmeOptions IAcmeOptions
} from '../../certificate/models/certificate-types.js'; } from '../../certificate/models/certificate-types.js';
/** /**
@ -35,8 +35,8 @@ export enum HttpStatus {
/** /**
* Represents a domain configuration with certificate status information * Represents a domain configuration with certificate status information
*/ */
export interface DomainCertificate { export interface IDomainCertificate {
options: DomainOptions; options: IDomainOptions;
certObtained: boolean; certObtained: boolean;
obtainingInProgress: boolean; obtainingInProgress: boolean;
certificate?: string; certificate?: string;
@ -82,7 +82,7 @@ export class ServerError extends HttpError {
/** /**
* Redirect configuration for HTTP requests * Redirect configuration for HTTP requests
*/ */
export interface RedirectConfig { export interface IRedirectConfig {
source: string; // Source path or pattern source: string; // Source path or pattern
destination: string; // Destination URL destination: string; // Destination URL
type: HttpStatus; // Redirect status code type: HttpStatus; // Redirect status code
@ -92,7 +92,7 @@ export interface RedirectConfig {
/** /**
* HTTP router configuration * HTTP router configuration
*/ */
export interface RouterConfig { export interface IRouterConfig {
routes: Array<{ routes: Array<{
path: string; path: string;
handler: (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void; handler: (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void;
@ -103,4 +103,3 @@ export interface RouterConfig {
// Backward compatibility interfaces // Backward compatibility interfaces
export { HttpError as Port80HandlerError }; export { HttpError as Port80HandlerError };
export { CertificateError as CertError }; export { CertificateError as CertError };
export type IDomainCertificate = DomainCertificate;

View File

@ -7,7 +7,7 @@ import * as plugins from '../../plugins.js';
/** /**
* Structure for SmartAcme certificate result * Structure for SmartAcme certificate result
*/ */
export interface SmartAcmeCert { export interface ISmartAcmeCert {
id?: string; id?: string;
domainName: string; domainName: string;
created?: number | Date | string; created?: number | Date | string;
@ -20,7 +20,7 @@ export interface SmartAcmeCert {
/** /**
* Structure for SmartAcme options * Structure for SmartAcme options
*/ */
export interface SmartAcmeOptions { export interface ISmartAcmeOptions {
accountEmail: string; accountEmail: string;
certManager: ICertManager; certManager: ICertManager;
environment: 'production' | 'integration'; environment: 'production' | 'integration';
@ -39,8 +39,8 @@ export interface SmartAcmeOptions {
*/ */
export interface ICertManager { export interface ICertManager {
init(): Promise<void>; init(): Promise<void>;
get(domainName: string): Promise<SmartAcmeCert | null>; get(domainName: string): Promise<ISmartAcmeCert | null>;
put(cert: SmartAcmeCert): Promise<SmartAcmeCert>; put(cert: ISmartAcmeCert): Promise<ISmartAcmeCert>;
delete(domainName: string): Promise<void>; delete(domainName: string): Promise<void>;
close?(): Promise<void>; close?(): Promise<void>;
} }
@ -59,7 +59,7 @@ export interface IChallengeHandler<T> {
/** /**
* HTTP-01 challenge type * HTTP-01 challenge type
*/ */
export interface Http01Challenge { export interface IHttp01Challenge {
type: string; // 'http-01' type: string; // 'http-01'
token: string; token: string;
keyAuthorization: string; keyAuthorization: string;
@ -69,17 +69,17 @@ export interface Http01Challenge {
/** /**
* HTTP-01 Memory Handler Interface * HTTP-01 Memory Handler Interface
*/ */
export interface Http01MemoryHandler extends IChallengeHandler<Http01Challenge> { export interface IHttp01MemoryHandler extends IChallengeHandler<IHttp01Challenge> {
handleRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse, next?: () => void): void; handleRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse, next?: () => void): void;
} }
/** /**
* SmartAcme main class interface * SmartAcme main class interface
*/ */
export interface SmartAcme { export interface ISmartAcme {
start(): Promise<void>; start(): Promise<void>;
stop(): Promise<void>; stop(): Promise<void>;
getCertificateForDomain(domain: string): Promise<SmartAcmeCert>; getCertificateForDomain(domain: string): Promise<ISmartAcmeCert>;
on?(event: string, listener: (data: any) => void): void; on?(event: string, listener: (data: any) => void): void;
eventEmitter?: plugins.EventEmitter; eventEmitter?: plugins.EventEmitter;
} }

View File

@ -4,15 +4,15 @@ import {
CertificateEvents CertificateEvents
} from '../../certificate/events/certificate-events.js'; } from '../../certificate/events/certificate-events.js';
import type { import type {
CertificateData, ICertificateData,
CertificateFailure, ICertificateFailure,
CertificateExpiring ICertificateExpiring
} from '../../certificate/models/certificate-types.js'; } from '../../certificate/models/certificate-types.js';
import type { import type {
SmartAcme, ISmartAcme,
SmartAcmeCert, ISmartAcmeCert,
SmartAcmeOptions, ISmartAcmeOptions,
Http01MemoryHandler IHttp01MemoryHandler
} from './acme-interfaces.js'; } from './acme-interfaces.js';
/** /**
@ -20,8 +20,8 @@ import type {
* It acts as a bridge between the HTTP server and the ACME challenge verification process * It acts as a bridge between the HTTP server and the ACME challenge verification process
*/ */
export class ChallengeResponder extends plugins.EventEmitter { export class ChallengeResponder extends plugins.EventEmitter {
private smartAcme: SmartAcme | null = null; private smartAcme: ISmartAcme | null = null;
private http01Handler: Http01MemoryHandler | null = null; private http01Handler: IHttp01MemoryHandler | null = null;
/** /**
* Creates a new challenge responder * Creates a new challenge responder
@ -95,7 +95,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
emitter.on('certificate', (data: any) => { emitter.on('certificate', (data: any) => {
const isRenewal = !!data.isRenewal; const isRenewal = !!data.isRenewal;
const certData: CertificateData = { const certData: ICertificateData = {
domain: data.domainName || data.domain, domain: data.domainName || data.domain,
certificate: data.publicKey || data.cert, certificate: data.publicKey || data.cert,
privateKey: data.privateKey || data.key, privateKey: data.privateKey || data.key,
@ -114,7 +114,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
// Forward error events // Forward error events
emitter.on('error', (error: any) => { emitter.on('error', (error: any) => {
const domain = error.domainName || error.domain || 'unknown'; const domain = error.domainName || error.domain || 'unknown';
const failureData: CertificateFailure = { const failureData: ICertificateFailure = {
domain, domain,
error: error.message || String(error), error: error.message || String(error),
isRenewal: !!error.isRenewal isRenewal: !!error.isRenewal
@ -171,7 +171,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
* @param domain Domain name to request a certificate for * @param domain Domain name to request a certificate for
* @param isRenewal Whether this is a renewal request * @param isRenewal Whether this is a renewal request
*/ */
public async requestCertificate(domain: string, isRenewal: boolean = false): Promise<CertificateData> { public async requestCertificate(domain: string, isRenewal: boolean = false): Promise<ICertificateData> {
if (!this.smartAcme) { if (!this.smartAcme) {
throw new Error('ACME client not initialized'); throw new Error('ACME client not initialized');
} }
@ -181,7 +181,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
const certObj = await this.smartAcme.getCertificateForDomain(domain); const certObj = await this.smartAcme.getCertificateForDomain(domain);
// Convert the certificate object to our CertificateData format // Convert the certificate object to our CertificateData format
const certData: CertificateData = { const certData: ICertificateData = {
domain, domain,
certificate: certObj.publicKey, certificate: certObj.publicKey,
privateKey: certObj.privateKey, privateKey: certObj.privateKey,
@ -193,7 +193,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
return certData; return certData;
} catch (error) { } catch (error) {
// Create failure object // Create failure object
const failure: CertificateFailure = { const failure: ICertificateFailure = {
domain, domain,
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
isRenewal isRenewal
@ -217,7 +217,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
*/ */
public checkCertificateExpiry( public checkCertificateExpiry(
domain: string, domain: string,
certificate: CertificateData, certificate: ICertificateData,
thresholdDays: number = 30 thresholdDays: number = 30
): void { ): void {
if (!certificate.expiryDate) return; if (!certificate.expiryDate) return;
@ -227,7 +227,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
const daysDifference = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); const daysDifference = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
if (daysDifference <= thresholdDays) { if (daysDifference <= thresholdDays) {
const expiryInfo: CertificateExpiring = { const expiryInfo: ICertificateExpiring = {
domain, domain,
expiryDate, expiryDate,
daysRemaining: daysDifference daysRemaining: daysDifference

View File

@ -2,12 +2,12 @@ import * as plugins from '../../plugins.js';
import { IncomingMessage, ServerResponse } from 'http'; import { IncomingMessage, ServerResponse } from 'http';
import { CertificateEvents } from '../../certificate/events/certificate-events.js'; import { CertificateEvents } from '../../certificate/events/certificate-events.js';
import type { import type {
ForwardConfig, IForwardConfig,
DomainOptions, IDomainOptions,
CertificateData, ICertificateData,
CertificateFailure, ICertificateFailure,
CertificateExpiring, ICertificateExpiring,
AcmeOptions IAcmeOptions
} from '../../certificate/models/certificate-types.js'; } from '../../certificate/models/certificate-types.js';
import { import {
HttpEvents, HttpEvents,
@ -16,7 +16,7 @@ import {
CertificateError, CertificateError,
ServerError, ServerError,
} from '../models/http-types.js'; } from '../models/http-types.js';
import type { DomainCertificate } from '../models/http-types.js'; import type { IDomainCertificate } from '../models/http-types.js';
import { ChallengeResponder } from './challenge-responder.js'; import { ChallengeResponder } from './challenge-responder.js';
// Re-export for backward compatibility // Re-export for backward compatibility
@ -40,21 +40,21 @@ export const Port80HandlerEvents = CertificateEvents;
* Now with glob pattern support for domain matching * Now with glob pattern support for domain matching
*/ */
export class Port80Handler extends plugins.EventEmitter { export class Port80Handler extends plugins.EventEmitter {
private domainCertificates: Map<string, DomainCertificate>; private domainCertificates: Map<string, IDomainCertificate>;
private challengeResponder: ChallengeResponder | null = null; private challengeResponder: ChallengeResponder | null = null;
private server: plugins.http.Server | null = null; private server: plugins.http.Server | null = null;
// Renewal scheduling is handled externally by SmartProxy // Renewal scheduling is handled externally by SmartProxy
private isShuttingDown: boolean = false; private isShuttingDown: boolean = false;
private options: Required<AcmeOptions>; private options: Required<IAcmeOptions>;
/** /**
* Creates a new Port80Handler * Creates a new Port80Handler
* @param options Configuration options * @param options Configuration options
*/ */
constructor(options: AcmeOptions = {}) { constructor(options: IAcmeOptions = {}) {
super(); super();
this.domainCertificates = new Map<string, DomainCertificate>(); this.domainCertificates = new Map<string, IDomainCertificate>();
// Default options // Default options
this.options = { this.options = {
@ -80,19 +80,19 @@ export class Port80Handler extends plugins.EventEmitter {
); );
// Forward certificate events from the challenge responder // Forward certificate events from the challenge responder
this.challengeResponder.on(CertificateEvents.CERTIFICATE_ISSUED, (data: CertificateData) => { this.challengeResponder.on(CertificateEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
this.emit(CertificateEvents.CERTIFICATE_ISSUED, data); this.emit(CertificateEvents.CERTIFICATE_ISSUED, data);
}); });
this.challengeResponder.on(CertificateEvents.CERTIFICATE_RENEWED, (data: CertificateData) => { this.challengeResponder.on(CertificateEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
this.emit(CertificateEvents.CERTIFICATE_RENEWED, data); this.emit(CertificateEvents.CERTIFICATE_RENEWED, data);
}); });
this.challengeResponder.on(CertificateEvents.CERTIFICATE_FAILED, (error: CertificateFailure) => { this.challengeResponder.on(CertificateEvents.CERTIFICATE_FAILED, (error: ICertificateFailure) => {
this.emit(CertificateEvents.CERTIFICATE_FAILED, error); this.emit(CertificateEvents.CERTIFICATE_FAILED, error);
}); });
this.challengeResponder.on(CertificateEvents.CERTIFICATE_EXPIRING, (expiry: CertificateExpiring) => { this.challengeResponder.on(CertificateEvents.CERTIFICATE_EXPIRING, (expiry: ICertificateExpiring) => {
this.emit(CertificateEvents.CERTIFICATE_EXPIRING, expiry); this.emit(CertificateEvents.CERTIFICATE_EXPIRING, expiry);
}); });
} }
@ -198,7 +198,7 @@ export class Port80Handler extends plugins.EventEmitter {
* Adds a domain with configuration options * Adds a domain with configuration options
* @param options Domain configuration options * @param options Domain configuration options
*/ */
public addDomain(options: DomainOptions): void { public addDomain(options: IDomainOptions): void {
if (!options.domainName || typeof options.domainName !== 'string') { if (!options.domainName || typeof options.domainName !== 'string') {
throw new HttpError('Invalid domain name'); throw new HttpError('Invalid domain name');
} }
@ -247,7 +247,7 @@ export class Port80Handler extends plugins.EventEmitter {
* Gets the certificate for a domain if it exists * Gets the certificate for a domain if it exists
* @param domain The domain to get the certificate for * @param domain The domain to get the certificate for
*/ */
public getCertificate(domain: string): CertificateData | null { public getCertificate(domain: string): ICertificateData | null {
// Can't get certificates for glob patterns // Can't get certificates for glob patterns
if (this.isGlobPattern(domain)) { if (this.isGlobPattern(domain)) {
return null; return null;
@ -283,7 +283,7 @@ export class Port80Handler extends plugins.EventEmitter {
* @param requestDomain The actual domain from the request * @param requestDomain The actual domain from the request
* @returns The domain info or null if not found * @returns The domain info or null if not found
*/ */
private getDomainInfoForRequest(requestDomain: string): { domainInfo: DomainCertificate, pattern: string } | null { private getDomainInfoForRequest(requestDomain: string): { domainInfo: IDomainCertificate, pattern: string } | null {
// Try direct match first // Try direct match first
if (this.domainCertificates.has(requestDomain)) { if (this.domainCertificates.has(requestDomain)) {
return { return {
@ -459,7 +459,7 @@ export class Port80Handler extends plugins.EventEmitter {
private forwardRequest( private forwardRequest(
req: plugins.http.IncomingMessage, req: plugins.http.IncomingMessage,
res: plugins.http.ServerResponse, res: plugins.http.ServerResponse,
target: ForwardConfig, target: IForwardConfig,
requestType: string requestType: string
): void { ): void {
const options = { const options = {
@ -612,7 +612,7 @@ export class Port80Handler extends plugins.EventEmitter {
* @param eventType The event type to emit * @param eventType The event type to emit
* @param data The certificate data * @param data The certificate data
*/ */
private emitCertificateEvent(eventType: CertificateEvents, data: CertificateData): void { private emitCertificateEvent(eventType: CertificateEvents, data: ICertificateData): void {
this.emit(eventType, data); this.emit(eventType, data);
} }

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { ReverseProxyConfig } from '../../proxies/network-proxy/models/types.js'; import type { IReverseProxyConfig } from '../../proxies/network-proxy/models/types.js';
/** /**
* Optional path pattern configuration that can be added to proxy configs * Optional path pattern configuration that can be added to proxy configs
@ -15,7 +15,7 @@ export type IPathPatternConfig = PathPatternConfig;
* Interface for router result with additional metadata * Interface for router result with additional metadata
*/ */
export interface RouterResult { export interface RouterResult {
config: ReverseProxyConfig; config: IReverseProxyConfig;
pathMatch?: string; pathMatch?: string;
pathParams?: Record<string, string>; pathParams?: Record<string, string>;
pathRemainder?: string; pathRemainder?: string;
@ -41,11 +41,11 @@ export type IRouterResult = RouterResult;
*/ */
export class ProxyRouter { export class ProxyRouter {
// Store original configs for reference // Store original configs for reference
private reverseProxyConfigs: ReverseProxyConfig[] = []; private reverseProxyConfigs: IReverseProxyConfig[] = [];
// Default config to use when no match is found (optional) // Default config to use when no match is found (optional)
private defaultConfig?: ReverseProxyConfig; private defaultConfig?: IReverseProxyConfig;
// Store path patterns separately since they're not in the original interface // Store path patterns separately since they're not in the original interface
private pathPatterns: Map<ReverseProxyConfig, string> = new Map(); private pathPatterns: Map<IReverseProxyConfig, string> = new Map();
// Logger interface // Logger interface
private logger: { private logger: {
error: (message: string, data?: any) => void; error: (message: string, data?: any) => void;
@ -55,7 +55,7 @@ export class ProxyRouter {
}; };
constructor( constructor(
configs?: ReverseProxyConfig[], configs?: IReverseProxyConfig[],
logger?: { logger?: {
error: (message: string, data?: any) => void; error: (message: string, data?: any) => void;
warn: (message: string, data?: any) => void; warn: (message: string, data?: any) => void;
@ -73,7 +73,7 @@ export class ProxyRouter {
* Sets a new set of reverse configs to be routed to * Sets a new set of reverse configs to be routed to
* @param reverseCandidatesArg Array of reverse proxy configurations * @param reverseCandidatesArg Array of reverse proxy configurations
*/ */
public setNewProxyConfigs(reverseCandidatesArg: ReverseProxyConfig[]): void { public setNewProxyConfigs(reverseCandidatesArg: IReverseProxyConfig[]): void {
this.reverseProxyConfigs = [...reverseCandidatesArg]; this.reverseProxyConfigs = [...reverseCandidatesArg];
// Find default config if any (config with "*" as hostname) // Find default config if any (config with "*" as hostname)
@ -87,7 +87,7 @@ export class ProxyRouter {
* @param req The incoming HTTP request * @param req The incoming HTTP request
* @returns The matching proxy config or undefined if no match found * @returns The matching proxy config or undefined if no match found
*/ */
public routeReq(req: plugins.http.IncomingMessage): ReverseProxyConfig { public routeReq(req: plugins.http.IncomingMessage): IReverseProxyConfig {
const result = this.routeReqWithDetails(req); const result = this.routeReqWithDetails(req);
return result ? result.config : undefined; return result ? result.config : undefined;
} }
@ -356,7 +356,7 @@ export class ProxyRouter {
* Gets all currently active proxy configurations * Gets all currently active proxy configurations
* @returns Array of all active configurations * @returns Array of all active configurations
*/ */
public getProxyConfigs(): ReverseProxyConfig[] { public getProxyConfigs(): IReverseProxyConfig[] {
return [...this.reverseProxyConfigs]; return [...this.reverseProxyConfigs];
} }
@ -380,7 +380,7 @@ export class ProxyRouter {
* @param pathPattern Optional path pattern for route matching * @param pathPattern Optional path pattern for route matching
*/ */
public addProxyConfig( public addProxyConfig(
config: ReverseProxyConfig, config: IReverseProxyConfig,
pathPattern?: string pathPattern?: string
): void { ): void {
this.reverseProxyConfigs.push(config); this.reverseProxyConfigs.push(config);
@ -398,7 +398,7 @@ export class ProxyRouter {
* @returns Boolean indicating if the config was found and updated * @returns Boolean indicating if the config was found and updated
*/ */
public setPathPattern( public setPathPattern(
config: ReverseProxyConfig, config: IReverseProxyConfig,
pathPattern: string pathPattern: string
): boolean { ): boolean {
const exists = this.reverseProxyConfigs.includes(config); const exists = this.reverseProxyConfigs.includes(config);

View File

@ -2,26 +2,26 @@ import * as plugins from '../../plugins.js';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { type NetworkProxyOptions, type CertificateEntry, type Logger, createLogger } from './models/types.js'; import { type INetworkProxyOptions, type ICertificateEntry, type ILogger, createLogger } from './models/types.js';
import { Port80Handler } from '../../http/port80/port80-handler.js'; import { Port80Handler } from '../../http/port80/port80-handler.js';
import { CertificateEvents } from '../../certificate/events/certificate-events.js'; import { CertificateEvents } from '../../certificate/events/certificate-events.js';
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js'; import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
import { subscribeToPort80Handler } from '../../core/utils/event-utils.js'; import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
import type { DomainOptions } from '../../certificate/models/certificate-types.js'; import type { IDomainOptions } from '../../certificate/models/certificate-types.js';
/** /**
* Manages SSL certificates for NetworkProxy including ACME integration * Manages SSL certificates for NetworkProxy including ACME integration
*/ */
export class CertificateManager { export class CertificateManager {
private defaultCertificates: { key: string; cert: string }; private defaultCertificates: { key: string; cert: string };
private certificateCache: Map<string, CertificateEntry> = new Map(); private certificateCache: Map<string, ICertificateEntry> = new Map();
private port80Handler: Port80Handler | null = null; private port80Handler: Port80Handler | null = null;
private externalPort80Handler: boolean = false; private externalPort80Handler: boolean = false;
private certificateStoreDir: string; private certificateStoreDir: string;
private logger: Logger; private logger: ILogger;
private httpsServer: plugins.https.Server | null = null; private httpsServer: plugins.https.Server | null = null;
constructor(private options: NetworkProxyOptions) { constructor(private options: INetworkProxyOptions) {
this.certificateStoreDir = path.resolve(options.acme?.certificateStore || './certs'); this.certificateStoreDir = path.resolve(options.acme?.certificateStore || './certs');
this.logger = createLogger(options.logLevel || 'info'); this.logger = createLogger(options.logLevel || 'info');
@ -221,7 +221,7 @@ export class CertificateManager {
this.logger.info(`No certificate found for ${domain}, registering for issuance`); this.logger.info(`No certificate found for ${domain}, registering for issuance`);
// Register with new domain options format // Register with new domain options format
const domainOptions: DomainOptions = { const domainOptions: IDomainOptions = {
domainName: domain, domainName: domain,
sslRedirect: true, sslRedirect: true,
acmeMaintenance: true acmeMaintenance: true
@ -274,7 +274,7 @@ export class CertificateManager {
/** /**
* Gets a certificate for a domain * Gets a certificate for a domain
*/ */
public getCertificate(domain: string): CertificateEntry | undefined { public getCertificate(domain: string): ICertificateEntry | undefined {
return this.certificateCache.get(domain); return this.certificateCache.get(domain);
} }
@ -300,7 +300,7 @@ export class CertificateManager {
try { try {
// Use the new domain options format // Use the new domain options format
const domainOptions: DomainOptions = { const domainOptions: IDomainOptions = {
domainName: domain, domainName: domain,
sslRedirect: true, sslRedirect: true,
acmeMaintenance: true acmeMaintenance: true
@ -341,7 +341,7 @@ export class CertificateManager {
} }
// Register the domain for certificate issuance with new domain options format // Register the domain for certificate issuance with new domain options format
const domainOptions: DomainOptions = { const domainOptions: IDomainOptions = {
domainName: domain, domainName: domain,
sslRedirect: true, sslRedirect: true,
acmeMaintenance: true acmeMaintenance: true

View File

@ -1,15 +1,15 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type ConnectionEntry, type Logger, createLogger } from './models/types.js'; import { type INetworkProxyOptions, type IConnectionEntry, type ILogger, createLogger } from './models/types.js';
/** /**
* Manages a pool of backend connections for efficient reuse * Manages a pool of backend connections for efficient reuse
*/ */
export class ConnectionPool { export class ConnectionPool {
private connectionPool: Map<string, Array<ConnectionEntry>> = new Map(); private connectionPool: Map<string, Array<IConnectionEntry>> = new Map();
private roundRobinPositions: Map<string, number> = new Map(); private roundRobinPositions: Map<string, number> = new Map();
private logger: Logger; private logger: ILogger;
constructor(private options: NetworkProxyOptions) { constructor(private options: INetworkProxyOptions) {
this.logger = createLogger(options.logLevel || 'info'); this.logger = createLogger(options.logLevel || 'info');
} }

View File

@ -1,10 +1,10 @@
import * as plugins from '../../../plugins.js'; import * as plugins from '../../../plugins.js';
import type { AcmeOptions } from '../../../certificate/models/certificate-types.js'; import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
/** /**
* Configuration options for NetworkProxy * Configuration options for NetworkProxy
*/ */
export interface NetworkProxyOptions { export interface INetworkProxyOptions {
port: number; port: number;
maxConnections?: number; maxConnections?: number;
keepAliveTimeout?: number; keepAliveTimeout?: number;
@ -25,13 +25,13 @@ export interface NetworkProxyOptions {
backendProtocol?: 'http1' | 'http2'; backendProtocol?: 'http1' | 'http2';
// ACME certificate management options // ACME certificate management options
acme?: AcmeOptions; acme?: IAcmeOptions;
} }
/** /**
* Interface for a certificate entry in the cache * Interface for a certificate entry in the cache
*/ */
export interface CertificateEntry { export interface ICertificateEntry {
key: string; key: string;
cert: string; cert: string;
expires?: Date; expires?: Date;
@ -40,7 +40,7 @@ export interface CertificateEntry {
/** /**
* Interface for reverse proxy configuration * Interface for reverse proxy configuration
*/ */
export interface ReverseProxyConfig { export interface IReverseProxyConfig {
destinationIps: string[]; destinationIps: string[];
destinationPorts: number[]; destinationPorts: number[];
hostName: string; hostName: string;
@ -62,7 +62,7 @@ export interface ReverseProxyConfig {
/** /**
* Interface for connection tracking in the pool * Interface for connection tracking in the pool
*/ */
export interface ConnectionEntry { export interface IConnectionEntry {
socket: plugins.net.Socket; socket: plugins.net.Socket;
lastUsed: number; lastUsed: number;
isIdle: boolean; isIdle: boolean;
@ -71,7 +71,7 @@ export interface ConnectionEntry {
/** /**
* WebSocket with heartbeat interface * WebSocket with heartbeat interface
*/ */
export interface WebSocketWithHeartbeat extends plugins.wsDefault { export interface IWebSocketWithHeartbeat extends plugins.wsDefault {
lastPong: number; lastPong: number;
isAlive: boolean; isAlive: boolean;
} }
@ -79,7 +79,7 @@ export interface WebSocketWithHeartbeat extends plugins.wsDefault {
/** /**
* Logger interface for consistent logging across components * Logger interface for consistent logging across components
*/ */
export interface Logger { export interface ILogger {
debug(message: string, data?: any): void; debug(message: string, data?: any): void;
info(message: string, data?: any): void; info(message: string, data?: any): void;
warn(message: string, data?: any): void; warn(message: string, data?: any): void;
@ -89,7 +89,7 @@ export interface Logger {
/** /**
* Creates a logger based on the specified log level * Creates a logger based on the specified log level
*/ */
export function createLogger(logLevel: string = 'info'): Logger { export function createLogger(logLevel: string = 'info'): ILogger {
const logLevels = { const logLevels = {
error: 0, error: 0,
warn: 1, warn: 1,
@ -120,11 +120,3 @@ export function createLogger(logLevel: string = 'info'): Logger {
} }
}; };
} }
// Backward compatibility interfaces
export interface INetworkProxyOptions extends NetworkProxyOptions {}
export interface ICertificateEntry extends CertificateEntry {}
export interface IReverseProxyConfig extends ReverseProxyConfig {}
export interface IConnectionEntry extends ConnectionEntry {}
export interface IWebSocketWithHeartbeat extends WebSocketWithHeartbeat {}
export interface ILogger extends Logger {}

View File

@ -3,9 +3,9 @@ import {
createLogger createLogger
} from './models/types.js'; } from './models/types.js';
import type { import type {
NetworkProxyOptions, INetworkProxyOptions,
Logger, ILogger,
ReverseProxyConfig IReverseProxyConfig
} from './models/types.js'; } from './models/types.js';
import { CertificateManager } from './certificate-manager.js'; import { CertificateManager } from './certificate-manager.js';
import { ConnectionPool } from './connection-pool.js'; import { ConnectionPool } from './connection-pool.js';
@ -24,8 +24,8 @@ export class NetworkProxy implements IMetricsTracker {
return {}; return {};
} }
// Configuration // Configuration
public options: NetworkProxyOptions; public options: INetworkProxyOptions;
public proxyConfigs: ReverseProxyConfig[] = []; public proxyConfigs: IReverseProxyConfig[] = [];
// Server instances (HTTP/2 with HTTP/1 fallback) // Server instances (HTTP/2 with HTTP/1 fallback)
public httpsServer: any; public httpsServer: any;
@ -54,12 +54,12 @@ export class NetworkProxy implements IMetricsTracker {
private connectionPoolCleanupInterval: NodeJS.Timeout; private connectionPoolCleanupInterval: NodeJS.Timeout;
// Logger // Logger
private logger: Logger; private logger: ILogger;
/** /**
* Creates a new NetworkProxy instance * Creates a new NetworkProxy instance
*/ */
constructor(optionsArg: NetworkProxyOptions) { constructor(optionsArg: INetworkProxyOptions) {
// Set default options // Set default options
this.options = { this.options = {
port: optionsArg.port, port: optionsArg.port,
@ -328,7 +328,7 @@ export class NetworkProxy implements IMetricsTracker {
* Updates proxy configurations * Updates proxy configurations
*/ */
public async updateProxyConfigs( public async updateProxyConfigs(
proxyConfigsArg: ReverseProxyConfig[] proxyConfigsArg: IReverseProxyConfig[]
): Promise<void> { ): Promise<void> {
this.logger.info(`Updating proxy configurations (${proxyConfigsArg.length} configs)`); this.logger.info(`Updating proxy configurations (${proxyConfigsArg.length} configs)`);
@ -385,8 +385,8 @@ export class NetworkProxy implements IMetricsTracker {
allowedIPs?: string[]; allowedIPs?: string[];
}>, }>,
sslKeyPair?: { key: string; cert: string } sslKeyPair?: { key: string; cert: string }
): ReverseProxyConfig[] { ): IReverseProxyConfig[] {
const proxyConfigs: ReverseProxyConfig[] = []; const proxyConfigs: IReverseProxyConfig[] = [];
// Use default certificates if not provided // Use default certificates if not provided
const defaultCerts = this.certificateManager.getDefaultCertificates(); const defaultCerts = this.certificateManager.getDefaultCertificates();
@ -478,7 +478,7 @@ export class NetworkProxy implements IMetricsTracker {
/** /**
* Gets all proxy configurations currently in use * Gets all proxy configurations currently in use
*/ */
public getProxyConfigs(): ReverseProxyConfig[] { public getProxyConfigs(): IReverseProxyConfig[] {
return [...this.proxyConfigs]; return [...this.proxyConfigs];
} }
} }

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type Logger, createLogger, type ReverseProxyConfig } from './models/types.js'; import { type INetworkProxyOptions, type ILogger, createLogger, type IReverseProxyConfig } from './models/types.js';
import { ConnectionPool } from './connection-pool.js'; import { ConnectionPool } from './connection-pool.js';
import { ProxyRouter } from '../../http/router/index.js'; import { ProxyRouter } from '../../http/router/index.js';
@ -19,13 +19,13 @@ export type MetricsTracker = IMetricsTracker;
*/ */
export class RequestHandler { export class RequestHandler {
private defaultHeaders: { [key: string]: string } = {}; private defaultHeaders: { [key: string]: string } = {};
private logger: Logger; private logger: ILogger;
private metricsTracker: IMetricsTracker | null = null; private metricsTracker: IMetricsTracker | null = null;
// HTTP/2 client sessions for backend proxying // HTTP/2 client sessions for backend proxying
private h2Sessions: Map<string, plugins.http2.ClientHttp2Session> = new Map(); private h2Sessions: Map<string, plugins.http2.ClientHttp2Session> = new Map();
constructor( constructor(
private options: NetworkProxyOptions, private options: INetworkProxyOptions,
private connectionPool: ConnectionPool, private connectionPool: ConnectionPool,
private router: ProxyRouter private router: ProxyRouter
) { ) {
@ -137,7 +137,7 @@ export class RequestHandler {
this.applyDefaultHeaders(res); this.applyDefaultHeaders(res);
// Determine routing configuration // Determine routing configuration
let proxyConfig: ReverseProxyConfig | undefined; let proxyConfig: IReverseProxyConfig | undefined;
try { try {
proxyConfig = this.router.routeReq(req); proxyConfig = this.router.routeReq(req);
} catch (err) { } catch (err) {
@ -235,7 +235,7 @@ export class RequestHandler {
// Remove host header to avoid issues with virtual hosts on target server // Remove host header to avoid issues with virtual hosts on target server
// The host header should match the target server's expected hostname // The host header should match the target server's expected hostname
if (options.headers && options.headers.host) { if (options.headers && options.headers.host) {
if ((proxyConfig as ReverseProxyConfig).rewriteHostHeader) { if ((proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
options.headers.host = `${destination.host}:${destination.port}`; options.headers.host = `${destination.host}:${destination.port}`;
} }
} }
@ -426,7 +426,7 @@ export class RequestHandler {
outboundHeaders[key] = value; outboundHeaders[key] = value;
} }
} }
if (outboundHeaders.host && (proxyConfig as any).rewriteHostHeader) { if (outboundHeaders.host && (proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
outboundHeaders.host = `${destination.host}:${destination.port}`; outboundHeaders.host = `${destination.host}:${destination.port}`;
} }
// Create HTTP/1 proxy request // Create HTTP/1 proxy request

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type WebSocketWithHeartbeat, type Logger, createLogger, type ReverseProxyConfig } from './models/types.js'; import { type INetworkProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger, type IReverseProxyConfig } from './models/types.js';
import { ConnectionPool } from './connection-pool.js'; import { ConnectionPool } from './connection-pool.js';
import { ProxyRouter } from '../../http/router/index.js'; import { ProxyRouter } from '../../http/router/index.js';
@ -9,10 +9,10 @@ import { ProxyRouter } from '../../http/router/index.js';
export class WebSocketHandler { export class WebSocketHandler {
private heartbeatInterval: NodeJS.Timeout | null = null; private heartbeatInterval: NodeJS.Timeout | null = null;
private wsServer: plugins.ws.WebSocketServer | null = null; private wsServer: plugins.ws.WebSocketServer | null = null;
private logger: Logger; private logger: ILogger;
constructor( constructor(
private options: NetworkProxyOptions, private options: INetworkProxyOptions,
private connectionPool: ConnectionPool, private connectionPool: ConnectionPool,
private router: ProxyRouter private router: ProxyRouter
) { ) {
@ -30,7 +30,7 @@ export class WebSocketHandler {
}); });
// Handle WebSocket connections // Handle WebSocket connections
this.wsServer.on('connection', (wsIncoming: WebSocketWithHeartbeat, req: plugins.http.IncomingMessage) => { this.wsServer.on('connection', (wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage) => {
this.handleWebSocketConnection(wsIncoming, req); this.handleWebSocketConnection(wsIncoming, req);
}); });
@ -58,7 +58,7 @@ export class WebSocketHandler {
this.logger.debug(`WebSocket heartbeat check for ${this.wsServer.clients.size} clients`); this.logger.debug(`WebSocket heartbeat check for ${this.wsServer.clients.size} clients`);
this.wsServer.clients.forEach((ws: plugins.wsDefault) => { this.wsServer.clients.forEach((ws: plugins.wsDefault) => {
const wsWithHeartbeat = ws as WebSocketWithHeartbeat; const wsWithHeartbeat = ws as IWebSocketWithHeartbeat;
if (wsWithHeartbeat.isAlive === false) { if (wsWithHeartbeat.isAlive === false) {
this.logger.debug('Terminating inactive WebSocket connection'); this.logger.debug('Terminating inactive WebSocket connection');
@ -79,7 +79,7 @@ export class WebSocketHandler {
/** /**
* Handle a new WebSocket connection * Handle a new WebSocket connection
*/ */
private handleWebSocketConnection(wsIncoming: WebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void { private handleWebSocketConnection(wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void {
try { try {
// Initialize heartbeat tracking // Initialize heartbeat tracking
wsIncoming.isAlive = true; wsIncoming.isAlive = true;
@ -127,7 +127,7 @@ export class WebSocketHandler {
} }
// Override host header if needed // Override host header if needed
if ((proxyConfig as ReverseProxyConfig).rewriteHostHeader) { if ((proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
headers['host'] = `${destination.host}:${destination.port}`; headers['host'] = `${destination.host}:${destination.port}`;
} }

View File

@ -1,8 +1,8 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { import type {
ConnectionRecord, IConnectionRecord,
DomainConfig, IDomainConfig,
SmartProxyOptions, ISmartProxyOptions,
} from './models/interfaces.js'; } from './models/interfaces.js';
import { ConnectionManager } from './connection-manager.js'; import { ConnectionManager } from './connection-manager.js';
import { SecurityManager } from './security-manager.js'; import { SecurityManager } from './security-manager.js';
@ -12,14 +12,14 @@ import { NetworkProxyBridge } from './network-proxy-bridge.js';
import { TimeoutManager } from './timeout-manager.js'; import { TimeoutManager } from './timeout-manager.js';
import { PortRangeManager } from './port-range-manager.js'; import { PortRangeManager } from './port-range-manager.js';
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js'; import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
import type { ForwardingType } from '../../forwarding/config/forwarding-types.js'; import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
/** /**
* Handles new connection processing and setup logic * Handles new connection processing and setup logic
*/ */
export class ConnectionHandler { export class ConnectionHandler {
constructor( constructor(
private settings: SmartProxyOptions, private settings: ISmartProxyOptions,
private connectionManager: ConnectionManager, private connectionManager: ConnectionManager,
private securityManager: SecurityManager, private securityManager: SecurityManager,
private domainConfigManager: DomainConfigManager, private domainConfigManager: DomainConfigManager,
@ -102,7 +102,7 @@ export class ConnectionHandler {
*/ */
private handleNetworkProxyConnection( private handleNetworkProxyConnection(
socket: plugins.net.Socket, socket: plugins.net.Socket,
record: ConnectionRecord record: IConnectionRecord
): void { ): void {
const connectionId = record.id; const connectionId = record.id;
let initialDataReceived = false; let initialDataReceived = false;
@ -307,7 +307,7 @@ export class ConnectionHandler {
/** /**
* Handle a standard (non-NetworkProxy) connection * Handle a standard (non-NetworkProxy) connection
*/ */
private handleStandardConnection(socket: plugins.net.Socket, record: ConnectionRecord): void { private handleStandardConnection(socket: plugins.net.Socket, record: IConnectionRecord): void {
const connectionId = record.id; const connectionId = record.id;
const localPort = record.localPort; const localPort = record.localPort;
@ -382,7 +382,7 @@ export class ConnectionHandler {
const setupConnection = ( const setupConnection = (
serverName: string, serverName: string,
initialChunk?: Buffer, initialChunk?: Buffer,
forcedDomain?: DomainConfig, forcedDomain?: IDomainConfig,
overridePort?: number overridePort?: number
) => { ) => {
// Clear the initial timeout since we've received data // Clear the initial timeout since we've received data
@ -500,7 +500,7 @@ export class ConnectionHandler {
const globalDomainConfig = { const globalDomainConfig = {
domains: ['global'], domains: ['global'],
forwarding: { forwarding: {
type: 'http-only' as ForwardingType, type: 'http-only' as TForwardingType,
target: { target: {
host: this.settings.targetIP!, host: this.settings.targetIP!,
port: this.settings.toPort port: this.settings.toPort
@ -730,8 +730,8 @@ export class ConnectionHandler {
*/ */
private setupDirectConnection( private setupDirectConnection(
socket: plugins.net.Socket, socket: plugins.net.Socket,
record: ConnectionRecord, record: IConnectionRecord,
domainConfig?: DomainConfig, domainConfig?: IDomainConfig,
serverName?: string, serverName?: string,
initialChunk?: Buffer, initialChunk?: Buffer,
overridePort?: number overridePort?: number

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { ConnectionRecord, SmartProxyOptions } from './models/interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
import { SecurityManager } from './security-manager.js'; import { SecurityManager } from './security-manager.js';
import { TimeoutManager } from './timeout-manager.js'; import { TimeoutManager } from './timeout-manager.js';
@ -7,14 +7,14 @@ import { TimeoutManager } from './timeout-manager.js';
* Manages connection lifecycle, tracking, and cleanup * Manages connection lifecycle, tracking, and cleanup
*/ */
export class ConnectionManager { export class ConnectionManager {
private connectionRecords: Map<string, ConnectionRecord> = new Map(); private connectionRecords: Map<string, IConnectionRecord> = new Map();
private terminationStats: { private terminationStats: {
incoming: Record<string, number>; incoming: Record<string, number>;
outgoing: Record<string, number>; outgoing: Record<string, number>;
} = { incoming: {}, outgoing: {} }; } = { incoming: {}, outgoing: {} };
constructor( constructor(
private settings: SmartProxyOptions, private settings: ISmartProxyOptions,
private securityManager: SecurityManager, private securityManager: SecurityManager,
private timeoutManager: TimeoutManager private timeoutManager: TimeoutManager
) {} ) {}
@ -30,12 +30,12 @@ export class ConnectionManager {
/** /**
* Create and track a new connection * Create and track a new connection
*/ */
public createConnection(socket: plugins.net.Socket): ConnectionRecord { public createConnection(socket: plugins.net.Socket): IConnectionRecord {
const connectionId = this.generateConnectionId(); const connectionId = this.generateConnectionId();
const remoteIP = socket.remoteAddress || ''; const remoteIP = socket.remoteAddress || '';
const localPort = socket.localPort || 0; const localPort = socket.localPort || 0;
const record: ConnectionRecord = { const record: IConnectionRecord = {
id: connectionId, id: connectionId,
incoming: socket, incoming: socket,
outgoing: null, outgoing: null,
@ -66,7 +66,7 @@ export class ConnectionManager {
/** /**
* Track an existing connection * Track an existing connection
*/ */
public trackConnection(connectionId: string, record: ConnectionRecord): void { public trackConnection(connectionId: string, record: IConnectionRecord): void {
this.connectionRecords.set(connectionId, record); this.connectionRecords.set(connectionId, record);
this.securityManager.trackConnectionByIP(record.remoteIP, connectionId); this.securityManager.trackConnectionByIP(record.remoteIP, connectionId);
} }
@ -74,14 +74,14 @@ export class ConnectionManager {
/** /**
* Get a connection by ID * Get a connection by ID
*/ */
public getConnection(connectionId: string): ConnectionRecord | undefined { public getConnection(connectionId: string): IConnectionRecord | undefined {
return this.connectionRecords.get(connectionId); return this.connectionRecords.get(connectionId);
} }
/** /**
* Get all active connections * Get all active connections
*/ */
public getConnections(): Map<string, ConnectionRecord> { public getConnections(): Map<string, IConnectionRecord> {
return this.connectionRecords; return this.connectionRecords;
} }
@ -95,7 +95,7 @@ export class ConnectionManager {
/** /**
* Initiates cleanup once for a connection * Initiates cleanup once for a connection
*/ */
public initiateCleanupOnce(record: ConnectionRecord, reason: string = 'normal'): void { public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
if (this.settings.enableDetailedLogging) { if (this.settings.enableDetailedLogging) {
console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`); console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`);
} }
@ -114,7 +114,7 @@ export class ConnectionManager {
/** /**
* Clean up a connection record * Clean up a connection record
*/ */
public cleanupConnection(record: ConnectionRecord, reason: string = 'normal'): void { public cleanupConnection(record: IConnectionRecord, reason: string = 'normal'): void {
if (!record.connectionClosed) { if (!record.connectionClosed) {
record.connectionClosed = true; record.connectionClosed = true;
@ -178,7 +178,7 @@ export class ConnectionManager {
/** /**
* Helper method to clean up a socket * Helper method to clean up a socket
*/ */
private cleanupSocket(record: ConnectionRecord, side: 'incoming' | 'outgoing', socket: plugins.net.Socket): void { private cleanupSocket(record: IConnectionRecord, side: 'incoming' | 'outgoing', socket: plugins.net.Socket): void {
try { try {
if (!socket.destroyed) { if (!socket.destroyed) {
// Try graceful shutdown first, then force destroy after a short timeout // Try graceful shutdown first, then force destroy after a short timeout
@ -213,7 +213,7 @@ export class ConnectionManager {
/** /**
* Creates a generic error handler for incoming or outgoing sockets * Creates a generic error handler for incoming or outgoing sockets
*/ */
public handleError(side: 'incoming' | 'outgoing', record: ConnectionRecord) { public handleError(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
return (err: Error) => { return (err: Error) => {
const code = (err as any).code; const code = (err as any).code;
let reason = 'error'; let reason = 'error';
@ -256,7 +256,7 @@ export class ConnectionManager {
/** /**
* Creates a generic close handler for incoming or outgoing sockets * Creates a generic close handler for incoming or outgoing sockets
*/ */
public handleClose(side: 'incoming' | 'outgoing', record: ConnectionRecord) { public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
return () => { return () => {
if (this.settings.enableDetailedLogging) { if (this.settings.enableDetailedLogging) {
console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`); console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`);

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { DomainConfig, SmartProxyOptions } from './models/interfaces.js'; import type { IDomainConfig, ISmartProxyOptions } from './models/interfaces.js';
import type { ForwardingType, ForwardConfig } from '../../forwarding/config/forwarding-types.js'; import type { TForwardingType, IForwardConfig } from '../../forwarding/config/forwarding-types.js';
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js'; import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-factory.js'; import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-factory.js';
@ -9,17 +9,17 @@ import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-fa
*/ */
export class DomainConfigManager { export class DomainConfigManager {
// Track round-robin indices for domain configs // Track round-robin indices for domain configs
private domainTargetIndices: Map<DomainConfig, number> = new Map(); private domainTargetIndices: Map<IDomainConfig, number> = new Map();
// Cache forwarding handlers for each domain config // Cache forwarding handlers for each domain config
private forwardingHandlers: Map<DomainConfig, ForwardingHandler> = new Map(); private forwardingHandlers: Map<IDomainConfig, ForwardingHandler> = new Map();
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Updates the domain configurations * Updates the domain configurations
*/ */
public updateDomainConfigs(newDomainConfigs: DomainConfig[]): void { public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void {
this.settings.domainConfigs = newDomainConfigs; this.settings.domainConfigs = newDomainConfigs;
// Reset target indices for removed configs // Reset target indices for removed configs
@ -31,7 +31,7 @@ export class DomainConfigManager {
} }
// Clear handlers for removed configs and create handlers for new configs // Clear handlers for removed configs and create handlers for new configs
const handlersToRemove: DomainConfig[] = []; const handlersToRemove: IDomainConfig[] = [];
for (const [config] of this.forwardingHandlers) { for (const [config] of this.forwardingHandlers) {
if (!currentConfigSet.has(config)) { if (!currentConfigSet.has(config)) {
handlersToRemove.push(config); handlersToRemove.push(config);
@ -59,14 +59,14 @@ export class DomainConfigManager {
/** /**
* Get all domain configurations * Get all domain configurations
*/ */
public getDomainConfigs(): DomainConfig[] { public getDomainConfigs(): IDomainConfig[] {
return this.settings.domainConfigs; return this.settings.domainConfigs;
} }
/** /**
* Find domain config matching a server name * Find domain config matching a server name
*/ */
public findDomainConfig(serverName: string): DomainConfig | undefined { public findDomainConfig(serverName: string): IDomainConfig | undefined {
if (!serverName) return undefined; if (!serverName) return undefined;
return this.settings.domainConfigs.find((config) => return this.settings.domainConfigs.find((config) =>
@ -77,7 +77,7 @@ export class DomainConfigManager {
/** /**
* Find domain config for a specific port * Find domain config for a specific port
*/ */
public findDomainConfigForPort(port: number): DomainConfig | undefined { public findDomainConfigForPort(port: number): IDomainConfig | undefined {
return this.settings.domainConfigs.find( return this.settings.domainConfigs.find(
(domain) => { (domain) => {
const portRanges = domain.forwarding?.advanced?.portRanges; const portRanges = domain.forwarding?.advanced?.portRanges;
@ -98,7 +98,7 @@ export class DomainConfigManager {
/** /**
* Get target IP with round-robin support * Get target IP with round-robin support
*/ */
public getTargetIP(domainConfig: DomainConfig): string { public getTargetIP(domainConfig: IDomainConfig): string {
const targetHosts = Array.isArray(domainConfig.forwarding.target.host) const targetHosts = Array.isArray(domainConfig.forwarding.target.host)
? domainConfig.forwarding.target.host ? domainConfig.forwarding.target.host
: [domainConfig.forwarding.target.host]; : [domainConfig.forwarding.target.host];
@ -117,21 +117,21 @@ export class DomainConfigManager {
* Get target host with round-robin support (for tests) * Get target host with round-robin support (for tests)
* This is just an alias for getTargetIP for easier test compatibility * This is just an alias for getTargetIP for easier test compatibility
*/ */
public getTargetHost(domainConfig: DomainConfig): string { public getTargetHost(domainConfig: IDomainConfig): string {
return this.getTargetIP(domainConfig); return this.getTargetIP(domainConfig);
} }
/** /**
* Get target port from domain config * Get target port from domain config
*/ */
public getTargetPort(domainConfig: DomainConfig, defaultPort: number): number { public getTargetPort(domainConfig: IDomainConfig, defaultPort: number): number {
return domainConfig.forwarding.target.port || defaultPort; return domainConfig.forwarding.target.port || defaultPort;
} }
/** /**
* Checks if a domain should use NetworkProxy * Checks if a domain should use NetworkProxy
*/ */
public shouldUseNetworkProxy(domainConfig: DomainConfig): boolean { public shouldUseNetworkProxy(domainConfig: IDomainConfig): boolean {
const forwardingType = this.getForwardingType(domainConfig); const forwardingType = this.getForwardingType(domainConfig);
return forwardingType === 'https-terminate-to-http' || return forwardingType === 'https-terminate-to-http' ||
forwardingType === 'https-terminate-to-https'; forwardingType === 'https-terminate-to-https';
@ -140,7 +140,7 @@ export class DomainConfigManager {
/** /**
* Gets the NetworkProxy port for a domain * Gets the NetworkProxy port for a domain
*/ */
public getNetworkProxyPort(domainConfig: DomainConfig): number | undefined { public getNetworkProxyPort(domainConfig: IDomainConfig): number | undefined {
// First check if we should use NetworkProxy at all // First check if we should use NetworkProxy at all
if (!this.shouldUseNetworkProxy(domainConfig)) { if (!this.shouldUseNetworkProxy(domainConfig)) {
return undefined; return undefined;
@ -155,7 +155,7 @@ export class DomainConfigManager {
* This method combines domain-specific security rules from the forwarding configuration * This method combines domain-specific security rules from the forwarding configuration
* with global security defaults when necessary. * with global security defaults when necessary.
*/ */
public getEffectiveIPRules(domainConfig: DomainConfig): { public getEffectiveIPRules(domainConfig: IDomainConfig): {
allowedIPs: string[], allowedIPs: string[],
blockedIPs: string[] blockedIPs: string[]
} { } {
@ -201,7 +201,7 @@ export class DomainConfigManager {
/** /**
* Get connection timeout for a domain * Get connection timeout for a domain
*/ */
public getConnectionTimeout(domainConfig?: DomainConfig): number { public getConnectionTimeout(domainConfig?: IDomainConfig): number {
if (domainConfig?.forwarding.advanced?.timeout) { if (domainConfig?.forwarding.advanced?.timeout) {
return domainConfig.forwarding.advanced.timeout; return domainConfig.forwarding.advanced.timeout;
} }
@ -212,7 +212,7 @@ export class DomainConfigManager {
/** /**
* Creates a forwarding handler for a domain configuration * Creates a forwarding handler for a domain configuration
*/ */
private createForwardingHandler(domainConfig: DomainConfig): ForwardingHandler { private createForwardingHandler(domainConfig: IDomainConfig): ForwardingHandler {
// Create a new handler using the factory // Create a new handler using the factory
const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding); const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding);
@ -228,7 +228,7 @@ export class DomainConfigManager {
* Gets a forwarding handler for a domain config * Gets a forwarding handler for a domain config
* If no handler exists, creates one * If no handler exists, creates one
*/ */
public getForwardingHandler(domainConfig: DomainConfig): ForwardingHandler { public getForwardingHandler(domainConfig: IDomainConfig): ForwardingHandler {
// If we already have a handler, return it // If we already have a handler, return it
if (this.forwardingHandlers.has(domainConfig)) { if (this.forwardingHandlers.has(domainConfig)) {
return this.forwardingHandlers.get(domainConfig)!; return this.forwardingHandlers.get(domainConfig)!;
@ -244,7 +244,7 @@ export class DomainConfigManager {
/** /**
* Gets the forwarding type for a domain config * Gets the forwarding type for a domain config
*/ */
public getForwardingType(domainConfig?: DomainConfig): ForwardingType | undefined { public getForwardingType(domainConfig?: IDomainConfig): TForwardingType | undefined {
if (!domainConfig?.forwarding) return undefined; if (!domainConfig?.forwarding) return undefined;
return domainConfig.forwarding.type; return domainConfig.forwarding.type;
} }
@ -252,7 +252,7 @@ export class DomainConfigManager {
/** /**
* Checks if the forwarding type requires TLS termination * Checks if the forwarding type requires TLS termination
*/ */
public requiresTlsTermination(domainConfig?: DomainConfig): boolean { public requiresTlsTermination(domainConfig?: IDomainConfig): boolean {
if (!domainConfig) return false; if (!domainConfig) return false;
const forwardingType = this.getForwardingType(domainConfig); const forwardingType = this.getForwardingType(domainConfig);
@ -263,7 +263,7 @@ export class DomainConfigManager {
/** /**
* Checks if the forwarding type supports HTTP * Checks if the forwarding type supports HTTP
*/ */
public supportsHttp(domainConfig?: DomainConfig): boolean { public supportsHttp(domainConfig?: IDomainConfig): boolean {
if (!domainConfig) return false; if (!domainConfig) return false;
const forwardingType = this.getForwardingType(domainConfig); const forwardingType = this.getForwardingType(domainConfig);
@ -285,7 +285,7 @@ export class DomainConfigManager {
/** /**
* Checks if HTTP requests should be redirected to HTTPS * Checks if HTTP requests should be redirected to HTTPS
*/ */
public shouldRedirectToHttps(domainConfig?: DomainConfig): boolean { public shouldRedirectToHttps(domainConfig?: IDomainConfig): boolean {
if (!domainConfig?.forwarding) return false; if (!domainConfig?.forwarding) return false;
// Only check for redirect if HTTP is enabled // Only check for redirect if HTTP is enabled

View File

@ -1,28 +1,28 @@
import * as plugins from '../../../plugins.js'; import * as plugins from '../../../plugins.js';
import type { ForwardConfig } from '../../../forwarding/config/forwarding-types.js'; import type { IForwardConfig } from '../../../forwarding/config/forwarding-types.js';
/** /**
* Provision object for static or HTTP-01 certificate * Provision object for static or HTTP-01 certificate
*/ */
export type SmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01'; export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
/** /**
* Domain configuration with forwarding configuration * Domain configuration with forwarding configuration
*/ */
export interface DomainConfig { export interface IDomainConfig {
domains: string[]; // Glob patterns for domain(s) domains: string[]; // Glob patterns for domain(s)
forwarding: ForwardConfig; // Unified forwarding configuration forwarding: IForwardConfig; // Unified forwarding configuration
} }
/** /**
* Configuration options for the SmartProxy * Configuration options for the SmartProxy
*/ */
import type { AcmeOptions } from '../../../certificate/models/certificate-types.js'; import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
export interface SmartProxyOptions { export interface ISmartProxyOptions {
fromPort: number; fromPort: number;
toPort: number; toPort: number;
targetIP?: string; // Global target host to proxy to, defaults to 'localhost' targetIP?: string; // Global target host to proxy to, defaults to 'localhost'
domainConfigs: DomainConfig[]; domainConfigs: IDomainConfig[];
sniEnabled?: boolean; sniEnabled?: boolean;
defaultAllowedIPs?: string[]; defaultAllowedIPs?: string[];
defaultBlockedIPs?: string[]; defaultBlockedIPs?: string[];
@ -81,19 +81,19 @@ export interface SmartProxyOptions {
networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443) networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
// ACME configuration options for SmartProxy // ACME configuration options for SmartProxy
acme?: AcmeOptions; acme?: IAcmeOptions;
/** /**
* Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges, * Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges,
* or a static certificate object for immediate provisioning. * or a static certificate object for immediate provisioning.
*/ */
certProvisionFunction?: (domain: string) => Promise<SmartProxyCertProvisionObject>; certProvisionFunction?: (domain: string) => Promise<TSmartProxyCertProvisionObject>;
} }
/** /**
* Enhanced connection record * Enhanced connection record
*/ */
export interface ConnectionRecord { export interface IConnectionRecord {
id: string; // Unique connection identifier id: string; // Unique connection identifier
incoming: plugins.net.Socket; incoming: plugins.net.Socket;
outgoing: plugins.net.Socket | null; outgoing: plugins.net.Socket | null;
@ -116,7 +116,7 @@ export interface ConnectionRecord {
isTLS: boolean; // Whether this connection is a TLS connection isTLS: boolean; // Whether this connection is a TLS connection
tlsHandshakeComplete: boolean; // Whether the TLS handshake is complete tlsHandshakeComplete: boolean; // Whether the TLS handshake is complete
hasReceivedInitialData: boolean; // Whether initial data has been received hasReceivedInitialData: boolean; // Whether initial data has been received
domainConfig?: DomainConfig; // Associated domain config for this connection domainConfig?: IDomainConfig; // Associated domain config for this connection
// Keep-alive tracking // Keep-alive tracking
hasKeepAlive: boolean; // Whether keep-alive is enabled for this connection hasKeepAlive: boolean; // Whether keep-alive is enabled for this connection
@ -134,9 +134,3 @@ export interface ConnectionRecord {
isBrowserConnection?: boolean; // Whether this connection appears to be from a browser isBrowserConnection?: boolean; // Whether this connection appears to be from a browser
domainSwitches?: number; // Number of times the domain has been switched on this connection domainSwitches?: number; // Number of times the domain has been switched on this connection
} }
// Backward compatibility types
export type ISmartProxyCertProvisionObject = SmartProxyCertProvisionObject;
export interface IDomainConfig extends DomainConfig {}
export interface ISmartProxyOptions extends SmartProxyOptions {}
export interface IConnectionRecord extends ConnectionRecord {}

View File

@ -3,8 +3,8 @@ import { NetworkProxy } from '../network-proxy/index.js';
import { Port80Handler } from '../../http/port80/port80-handler.js'; import { Port80Handler } from '../../http/port80/port80-handler.js';
import { Port80HandlerEvents } from '../../core/models/common-types.js'; import { Port80HandlerEvents } from '../../core/models/common-types.js';
import { subscribeToPort80Handler } from '../../core/utils/event-utils.js'; import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
import type { CertificateData } from '../../certificate/models/certificate-types.js'; import type { ICertificateData } from '../../certificate/models/certificate-types.js';
import type { ConnectionRecord, SmartProxyOptions, DomainConfig } from './models/interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
/** /**
* Manages NetworkProxy integration for TLS termination * Manages NetworkProxy integration for TLS termination
@ -13,7 +13,7 @@ export class NetworkProxyBridge {
private networkProxy: NetworkProxy | null = null; private networkProxy: NetworkProxy | null = null;
private port80Handler: Port80Handler | null = null; private port80Handler: Port80Handler | null = null;
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Set the Port80Handler to use for certificate management * Set the Port80Handler to use for certificate management
@ -66,7 +66,7 @@ export class NetworkProxyBridge {
/** /**
* Handle certificate issuance or renewal events * Handle certificate issuance or renewal events
*/ */
private handleCertificateEvent(data: CertificateData): void { private handleCertificateEvent(data: ICertificateData): void {
if (!this.networkProxy) return; if (!this.networkProxy) return;
console.log(`Received certificate for ${data.domain} from Port80Handler, updating NetworkProxy`); console.log(`Received certificate for ${data.domain} from Port80Handler, updating NetworkProxy`);
@ -99,7 +99,7 @@ export class NetworkProxyBridge {
/** /**
* Apply an external (static) certificate into NetworkProxy * Apply an external (static) certificate into NetworkProxy
*/ */
public applyExternalCertificate(data: CertificateData): void { public applyExternalCertificate(data: ICertificateData): void {
if (!this.networkProxy) { if (!this.networkProxy) {
console.log(`NetworkProxy not initialized: cannot apply external certificate for ${data.domain}`); console.log(`NetworkProxy not initialized: cannot apply external certificate for ${data.domain}`);
return; return;
@ -183,7 +183,7 @@ export class NetworkProxyBridge {
public forwardToNetworkProxy( public forwardToNetworkProxy(
connectionId: string, connectionId: string,
socket: plugins.net.Socket, socket: plugins.net.Socket,
record: ConnectionRecord, record: IConnectionRecord,
initialData: Buffer, initialData: Buffer,
customProxyPort?: number, customProxyPort?: number,
onError?: (reason: string) => void onError?: (reason: string) => void

View File

@ -1,10 +1,10 @@
import type { SmartProxyOptions } from './models/interfaces.js'; import type { ISmartProxyOptions } from './models/interfaces.js';
/** /**
* Manages port ranges and port-based configuration * Manages port ranges and port-based configuration
*/ */
export class PortRangeManager { export class PortRangeManager {
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Get all ports that should be listened on * Get all ports that should be listened on

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { SmartProxyOptions } from './models/interfaces.js'; import type { ISmartProxyOptions } from './models/interfaces.js';
/** /**
* Handles security aspects like IP tracking, rate limiting, and authorization * Handles security aspects like IP tracking, rate limiting, and authorization
@ -8,7 +8,7 @@ export class SecurityManager {
private connectionsByIP: Map<string, Set<string>> = new Map(); private connectionsByIP: Map<string, Set<string>> = new Map();
private connectionRateByIP: Map<string, number[]> = new Map(); private connectionRateByIP: Map<string, number[]> = new Map();
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Get connections count by IP * Get connections count by IP

View File

@ -13,15 +13,15 @@ import { ConnectionHandler } from './connection-handler.js';
// External dependencies from migrated modules // External dependencies from migrated modules
import { Port80Handler } from '../../http/port80/port80-handler.js'; import { Port80Handler } from '../../http/port80/port80-handler.js';
import { CertProvisioner } from '../../certificate/providers/cert-provisioner.js'; import { CertProvisioner } from '../../certificate/providers/cert-provisioner.js';
import type { CertificateData } from '../../certificate/models/certificate-types.js'; import type { ICertificateData } from '../../certificate/models/certificate-types.js';
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js'; import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
import type { ForwardingType } from '../../forwarding/config/forwarding-types.js'; import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
import { createPort80HandlerOptions } from '../../common/port80-adapter.js'; import { createPort80HandlerOptions } from '../../common/port80-adapter.js';
// Import types from models // Import types from models
import type { SmartProxyOptions, DomainConfig } from './models/interfaces.js'; import type { ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
// Provide backward compatibility types // Provide backward compatibility types
export type { SmartProxyOptions as IPortProxySettings, DomainConfig as IDomainConfig }; export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
/** /**
* SmartProxy - Main class that coordinates all components * SmartProxy - Main class that coordinates all components
@ -46,7 +46,7 @@ export class SmartProxy extends plugins.EventEmitter {
// CertProvisioner for unified certificate workflows // CertProvisioner for unified certificate workflows
private certProvisioner?: CertProvisioner; private certProvisioner?: CertProvisioner;
constructor(settingsArg: SmartProxyOptions) { constructor(settingsArg: ISmartProxyOptions) {
super(); super();
// Set reasonable defaults for all settings // Set reasonable defaults for all settings
this.settings = { this.settings = {
@ -126,7 +126,7 @@ export class SmartProxy extends plugins.EventEmitter {
/** /**
* The settings for the port proxy * The settings for the port proxy
*/ */
public settings: SmartProxyOptions; public settings: ISmartProxyOptions;
/** /**
* Initialize the Port80Handler for ACME certificate management * Initialize the Port80Handler for ACME certificate management
@ -413,7 +413,7 @@ export class SmartProxy extends plugins.EventEmitter {
/** /**
* Updates the domain configurations for the proxy * Updates the domain configurations for the proxy
*/ */
public async updateDomainConfigs(newDomainConfigs: DomainConfig[]): Promise<void> { public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`); console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
// Update domain configs in DomainConfigManager // Update domain configs in DomainConfigManager
@ -475,7 +475,7 @@ export class SmartProxy extends plugins.EventEmitter {
} else { } else {
// Static certificate (e.g., DNS-01 provisioned) supports wildcards // Static certificate (e.g., DNS-01 provisioned) supports wildcards
const certObj = provision as plugins.tsclass.network.ICert; const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = { const certData: ICertificateData = {
domain: certObj.domainName, domain: certObj.domainName,
certificate: certObj.publicKey, certificate: certObj.publicKey,
privateKey: certObj.privateKey, privateKey: certObj.privateKey,

View File

@ -1,10 +1,10 @@
import type { ConnectionRecord, SmartProxyOptions } from './models/interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
/** /**
* Manages timeouts and inactivity tracking for connections * Manages timeouts and inactivity tracking for connections
*/ */
export class TimeoutManager { export class TimeoutManager {
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Ensure timeout values don't exceed Node.js max safe integer * Ensure timeout values don't exceed Node.js max safe integer
@ -28,7 +28,7 @@ export class TimeoutManager {
/** /**
* Update connection activity timestamp * Update connection activity timestamp
*/ */
public updateActivity(record: ConnectionRecord): void { public updateActivity(record: IConnectionRecord): void {
record.lastActivity = Date.now(); record.lastActivity = Date.now();
// Clear any inactivity warning // Clear any inactivity warning
@ -40,7 +40,7 @@ export class TimeoutManager {
/** /**
* Calculate effective inactivity timeout based on connection type * Calculate effective inactivity timeout based on connection type
*/ */
public getEffectiveInactivityTimeout(record: ConnectionRecord): number { public getEffectiveInactivityTimeout(record: IConnectionRecord): number {
let effectiveTimeout = this.settings.inactivityTimeout || 14400000; // 4 hours default let effectiveTimeout = this.settings.inactivityTimeout || 14400000; // 4 hours default
// For immortal keep-alive connections, use an extremely long timeout // For immortal keep-alive connections, use an extremely long timeout
@ -60,7 +60,7 @@ export class TimeoutManager {
/** /**
* Calculate effective max lifetime based on connection type * Calculate effective max lifetime based on connection type
*/ */
public getEffectiveMaxLifetime(record: ConnectionRecord): number { public getEffectiveMaxLifetime(record: IConnectionRecord): number {
// Use domain-specific timeout from forwarding.advanced if available // Use domain-specific timeout from forwarding.advanced if available
const baseTimeout = record.domainConfig?.forwarding?.advanced?.timeout || const baseTimeout = record.domainConfig?.forwarding?.advanced?.timeout ||
this.settings.maxConnectionLifetime || this.settings.maxConnectionLifetime ||
@ -91,8 +91,8 @@ export class TimeoutManager {
* @returns The cleanup timer * @returns The cleanup timer
*/ */
public setupConnectionTimeout( public setupConnectionTimeout(
record: ConnectionRecord, record: IConnectionRecord,
onTimeout: (record: ConnectionRecord, reason: string) => void onTimeout: (record: IConnectionRecord, reason: string) => void
): NodeJS.Timeout { ): NodeJS.Timeout {
// Clear any existing timer // Clear any existing timer
if (record.cleanupTimer) { if (record.cleanupTimer) {
@ -120,7 +120,7 @@ export class TimeoutManager {
* Check for inactivity on a connection * Check for inactivity on a connection
* @returns Object with check results * @returns Object with check results
*/ */
public checkInactivity(record: ConnectionRecord): { public checkInactivity(record: IConnectionRecord): {
isInactive: boolean; isInactive: boolean;
shouldWarn: boolean; shouldWarn: boolean;
inactivityTime: number; inactivityTime: number;
@ -169,7 +169,7 @@ export class TimeoutManager {
/** /**
* Apply socket timeout settings * Apply socket timeout settings
*/ */
public applySocketTimeouts(record: ConnectionRecord): void { public applySocketTimeouts(record: IConnectionRecord): void {
// Skip for immortal keep-alive connections // Skip for immortal keep-alive connections
if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal') { if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal') {
// Disable timeouts completely for immortal connections // Disable timeouts completely for immortal connections

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { SmartProxyOptions } from './models/interfaces.js'; import type { ISmartProxyOptions } from './models/interfaces.js';
import { SniHandler } from '../../tls/sni/sni-handler.js'; import { SniHandler } from '../../tls/sni/sni-handler.js';
/** /**
@ -16,7 +16,7 @@ interface IConnectionInfo {
* Manages TLS-related operations including SNI extraction and validation * Manages TLS-related operations including SNI extraction and validation
*/ */
export class TlsManager { export class TlsManager {
constructor(private settings: SmartProxyOptions) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Check if a data chunk appears to be a TLS handshake * Check if a data chunk appears to be a TLS handshake