fix(typescript): Refactor types and interfaces to use consistent I prefix and update related tests
This commit is contained in:
parent
d924190680
commit
f8647516b5
@ -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
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
@ -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: {
|
||||||
|
@ -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 }
|
||||||
|
@ -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 }
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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.'
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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');
|
||||||
|
@ -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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {}
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 {}
|
|
@ -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');
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
|
@ -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;
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {}
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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}`);
|
||||||
|
@ -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
|
||||||
|
@ -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 {}
|
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user