2025-05-07 23:04:54 +00:00
|
|
|
# Implementation Hints and Learnings
|
|
|
|
|
|
|
|
## SmartProxy Usage
|
|
|
|
|
2025-05-16 15:50:46 +00:00
|
|
|
### New Route-Based Architecture (v18+)
|
|
|
|
- SmartProxy now uses a route-based configuration system
|
|
|
|
- Routes define match criteria and actions instead of simple port-to-port forwarding
|
|
|
|
- All traffic types (HTTP, HTTPS, TCP, WebSocket) are configured through routes
|
2025-05-07 23:04:54 +00:00
|
|
|
|
|
|
|
```typescript
|
2025-05-16 15:50:46 +00:00
|
|
|
// NEW: Route-based SmartProxy configuration
|
2025-05-07 23:04:54 +00:00
|
|
|
const smartProxy = new plugins.smartproxy.SmartProxy({
|
2025-05-16 15:50:46 +00:00
|
|
|
routes: [
|
|
|
|
{
|
|
|
|
name: 'https-traffic',
|
|
|
|
match: {
|
|
|
|
ports: 443,
|
|
|
|
domains: ['example.com', '*.example.com']
|
|
|
|
},
|
|
|
|
action: {
|
|
|
|
type: 'forward',
|
|
|
|
target: {
|
|
|
|
host: 'backend.server.com',
|
|
|
|
port: 8080
|
|
|
|
}
|
|
|
|
},
|
|
|
|
tls: {
|
|
|
|
mode: 'terminate',
|
|
|
|
certificate: 'auto'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
defaults: {
|
|
|
|
target: {
|
|
|
|
host: 'fallback.server.com',
|
|
|
|
port: 8080
|
|
|
|
}
|
|
|
|
},
|
2025-05-07 23:04:54 +00:00
|
|
|
acme: {
|
2025-05-16 15:50:46 +00:00
|
|
|
accountEmail: 'admin@example.com',
|
2025-05-07 23:04:54 +00:00
|
|
|
enabled: true,
|
2025-05-16 15:50:46 +00:00
|
|
|
useProduction: true
|
|
|
|
}
|
2025-05-07 23:04:54 +00:00
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2025-05-16 15:50:46 +00:00
|
|
|
### Migration from Old to New
|
|
|
|
```typescript
|
|
|
|
// OLD configuration style (deprecated)
|
|
|
|
{
|
|
|
|
fromPort: 443,
|
|
|
|
toPort: 8080,
|
|
|
|
targetIP: 'backend.server.com',
|
|
|
|
domainConfigs: [...]
|
|
|
|
}
|
|
|
|
|
|
|
|
// NEW route-based style
|
|
|
|
{
|
|
|
|
routes: [{
|
|
|
|
name: 'main-route',
|
|
|
|
match: { ports: 443 },
|
|
|
|
action: {
|
|
|
|
type: 'forward',
|
|
|
|
target: { host: 'backend.server.com', port: 8080 }
|
|
|
|
}
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Direct Component Usage
|
|
|
|
- Use SmartProxy components directly instead of creating your own wrappers
|
|
|
|
- SmartProxy already includes Port80Handler and NetworkProxy functionality
|
|
|
|
- When using SmartProxy, configure it directly rather than instantiating Port80Handler or NetworkProxy separately
|
|
|
|
|
2025-05-07 23:04:54 +00:00
|
|
|
### Certificate Management
|
|
|
|
- SmartProxy has built-in ACME certificate management
|
|
|
|
- Configure it in the `acme` property of SmartProxy options
|
|
|
|
- Use `accountEmail` (not `email`) for the ACME contact email
|
|
|
|
- SmartProxy handles both HTTP-01 challenges and certificate application automatically
|
|
|
|
|
|
|
|
## qenv Usage
|
|
|
|
|
|
|
|
### Direct Usage
|
|
|
|
- Use qenv directly instead of creating environment variable wrappers
|
|
|
|
- Instantiate qenv with appropriate basePath and nogitPath:
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
const qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
|
|
|
const value = await qenv.getEnvVarOnDemand('ENV_VAR_NAME');
|
|
|
|
```
|
|
|
|
|
|
|
|
## TypeScript Interfaces
|
|
|
|
|
|
|
|
### SmartProxy Interfaces
|
|
|
|
- Always check the interfaces from the node_modules to ensure correct property names
|
2025-05-16 15:50:46 +00:00
|
|
|
- Important interfaces for the new architecture:
|
|
|
|
- `ISmartProxyOptions`: Main configuration with `routes` array
|
|
|
|
- `IRouteConfig`: Individual route configuration
|
|
|
|
- `IRouteMatch`: Match criteria for routes
|
|
|
|
- `IRouteTarget`: Target configuration for forwarding
|
2025-05-07 23:04:54 +00:00
|
|
|
- `IAcmeOptions`: ACME certificate configuration
|
2025-05-16 15:50:46 +00:00
|
|
|
- `TTlsMode`: TLS handling modes ('passthrough' | 'terminate' | 'terminate-and-reencrypt')
|
|
|
|
|
|
|
|
### New Route Configuration
|
|
|
|
```typescript
|
|
|
|
interface IRouteConfig {
|
|
|
|
name: string;
|
|
|
|
match: {
|
|
|
|
ports: number | number[];
|
|
|
|
domains?: string | string[];
|
|
|
|
path?: string;
|
|
|
|
headers?: Record<string, string | RegExp>;
|
|
|
|
};
|
|
|
|
action: {
|
|
|
|
type: 'forward' | 'redirect' | 'block' | 'static';
|
|
|
|
target?: {
|
|
|
|
host: string | string[] | ((context) => string);
|
|
|
|
port: number | 'preserve' | ((context) => number);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
tls?: {
|
|
|
|
mode: TTlsMode;
|
|
|
|
certificate?: 'auto' | { key: string; cert: string; };
|
|
|
|
};
|
|
|
|
security?: {
|
|
|
|
authentication?: IRouteAuthentication;
|
|
|
|
rateLimit?: IRouteRateLimit;
|
|
|
|
ipAllowList?: string[];
|
|
|
|
ipBlockList?: string[];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
2025-05-07 23:04:54 +00:00
|
|
|
|
|
|
|
### Required Properties
|
2025-05-16 15:50:46 +00:00
|
|
|
- For `ISmartProxyOptions`, `routes` array is the main configuration
|
2025-05-07 23:04:54 +00:00
|
|
|
- For `IAcmeOptions`, use `accountEmail` for the contact email
|
2025-05-16 15:50:46 +00:00
|
|
|
- Routes must have `name`, `match`, and `action` properties
|
2025-05-07 23:04:54 +00:00
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
|
|
### Test Structure
|
|
|
|
- Follow the project's test structure, using `@push.rocks/tapbundle`
|
|
|
|
- Use `expect(value).toEqual(expected)` for equality checks
|
|
|
|
- Use `expect(value).toBeTruthy()` for boolean assertions
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
tap.test('test description', async () => {
|
|
|
|
const result = someFunction();
|
|
|
|
expect(result.property).toEqual('expected value');
|
|
|
|
expect(result.valid).toBeTruthy();
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
### Cleanup
|
|
|
|
- Include a cleanup test to ensure proper test resource handling
|
|
|
|
- Add a `stop` test to forcefully end the test when needed:
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
tap.test('stop', async () => {
|
|
|
|
await tap.stopForcefully();
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
## Architecture Principles
|
|
|
|
|
|
|
|
### Simplicity
|
|
|
|
- Prefer direct usage of libraries instead of creating wrappers
|
|
|
|
- Don't reinvent functionality that already exists in dependencies
|
|
|
|
- Keep interfaces clean and focused, avoiding unnecessary abstraction layers
|
|
|
|
|
|
|
|
### Component Integration
|
|
|
|
- Leverage built-in integrations between components (like SmartProxy's ACME handling)
|
|
|
|
- Use parallel operations for performance (like in the `stop()` method)
|
2025-05-16 15:50:46 +00:00
|
|
|
- Separate concerns clearly (HTTP handling vs. SMTP handling)
|
|
|
|
|
|
|
|
## Email Integration with SmartProxy
|
|
|
|
|
|
|
|
### Architecture
|
|
|
|
- Email traffic is routed through SmartProxy using automatic route generation
|
|
|
|
- Email server runs on internal ports and receives forwarded traffic from SmartProxy
|
|
|
|
- SmartProxy handles external ports (25, 587, 465) and forwards to internal ports
|
|
|
|
|
|
|
|
### Email Route Generation
|
|
|
|
```typescript
|
|
|
|
// Email configuration automatically generates SmartProxy routes
|
|
|
|
emailConfig: {
|
|
|
|
ports: [25, 587, 465],
|
|
|
|
hostname: 'mail.example.com',
|
|
|
|
domainRules: [...]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates routes like:
|
|
|
|
{
|
|
|
|
name: 'smtp-route',
|
|
|
|
match: { ports: [25] },
|
|
|
|
action: {
|
|
|
|
type: 'forward',
|
|
|
|
target: { host: 'localhost', port: 10025 }
|
|
|
|
},
|
|
|
|
tls: { mode: 'passthrough' } // STARTTLS handled by email server
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Port Mapping
|
|
|
|
- External port 25 → Internal port 10025 (SMTP)
|
|
|
|
- External port 587 → Internal port 10587 (Submission)
|
|
|
|
- External port 465 → Internal port 10465 (SMTPS)
|
|
|
|
|
|
|
|
### TLS Handling
|
|
|
|
- Ports 25 and 587: Use 'passthrough' mode (STARTTLS handled by email server)
|
|
|
|
- Port 465: Use 'terminate' mode (SmartProxy handles TLS termination)
|
|
|
|
- Domain-specific TLS can be configured per email rule
|