@digitaldefiance/node-express-suite 3.12.11 → 3.12.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +6 -3
  2. package/src/interfaces/index.d.ts +1 -0
  3. package/src/interfaces/index.d.ts.map +1 -1
  4. package/src/interfaces/index.js +1 -0
  5. package/src/interfaces/index.js.map +1 -1
  6. package/src/interfaces/network/index.d.ts +3 -0
  7. package/src/interfaces/network/index.d.ts.map +1 -0
  8. package/src/interfaces/network/index.js +6 -0
  9. package/src/interfaces/network/index.js.map +1 -0
  10. package/src/interfaces/network/upnpService.d.ts +86 -0
  11. package/src/interfaces/network/upnpService.d.ts.map +1 -0
  12. package/src/interfaces/network/upnpService.js +3 -0
  13. package/src/interfaces/network/upnpService.js.map +1 -0
  14. package/src/interfaces/network/upnpTypes.d.ts +120 -0
  15. package/src/interfaces/network/upnpTypes.d.ts.map +1 -0
  16. package/src/interfaces/network/upnpTypes.js +57 -0
  17. package/src/interfaces/network/upnpTypes.js.map +1 -0
  18. package/src/plugins/index.d.ts +1 -0
  19. package/src/plugins/index.d.ts.map +1 -1
  20. package/src/plugins/index.js +1 -0
  21. package/src/plugins/index.js.map +1 -1
  22. package/src/plugins/upnp.d.ts +129 -0
  23. package/src/plugins/upnp.d.ts.map +1 -0
  24. package/src/plugins/upnp.js +158 -0
  25. package/src/plugins/upnp.js.map +1 -0
  26. package/src/services/index.d.ts +3 -0
  27. package/src/services/index.d.ts.map +1 -1
  28. package/src/services/index.js +3 -0
  29. package/src/services/index.js.map +1 -1
  30. package/src/services/upnp-config.d.ts +131 -0
  31. package/src/services/upnp-config.d.ts.map +1 -0
  32. package/src/services/upnp-config.js +225 -0
  33. package/src/services/upnp-config.js.map +1 -0
  34. package/src/services/upnp-manager.d.ts +211 -0
  35. package/src/services/upnp-manager.d.ts.map +1 -0
  36. package/src/services/upnp-manager.js +447 -0
  37. package/src/services/upnp-manager.js.map +1 -0
  38. package/src/services/upnp.d.ts +241 -0
  39. package/src/services/upnp.d.ts.map +1 -0
  40. package/src/services/upnp.js +415 -0
  41. package/src/services/upnp.js.map +1 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * UPnP Manager for Express server integration.
3
+ *
4
+ * Orchestrates UPnP port mapping lifecycle: initialization, periodic refresh,
5
+ * health monitoring, and graceful shutdown. Wraps the core UpnpService with
6
+ * server-specific concerns like signal handling and exponential backoff on
7
+ * refresh failures.
8
+ *
9
+ * All UPnP failures are non-fatal — the server continues operating even if
10
+ * port mapping fails, with appropriate log messages for manual intervention.
11
+ *
12
+ * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 4.10
13
+ */
14
+ import { IUpnpConfig } from '../interfaces/network/upnpTypes';
15
+ import { UpnpConfig } from './upnp-config';
16
+ /**
17
+ * Options for constructing a UpnpManager.
18
+ *
19
+ * Allows passing a config along with an optional description prefix
20
+ * for port mapping labels.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const manager = new UpnpManager({
25
+ * config: UpnpConfig.fromEnvironment(),
26
+ * descriptionPrefix: 'My App',
27
+ * });
28
+ * ```
29
+ */
30
+ export interface UpnpManagerOptions {
31
+ /** UPnP configuration (IUpnpConfig or UpnpConfig instance) */
32
+ config: IUpnpConfig | UpnpConfig;
33
+ /** Prefix for port mapping descriptions (defaults to 'Express App') */
34
+ descriptionPrefix?: string;
35
+ }
36
+ /**
37
+ * Manages UPnP port mappings for the Express server lifecycle.
38
+ *
39
+ * Handles:
40
+ * - Creating HTTP port mapping on startup
41
+ * - Creating WebSocket port mapping when on a separate port
42
+ * - Removing mappings on shutdown
43
+ * - Periodic health monitoring and refresh
44
+ * - SIGTERM/SIGINT signal handling
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const config = UpnpConfig.fromEnvironment();
49
+ * const manager = new UpnpManager(config);
50
+ *
51
+ * await manager.initialize(); // creates mapping, starts refresh timer
52
+ * // ... server runs ...
53
+ * await manager.shutdown(); // removes mappings, cleans up
54
+ * ```
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // With custom description prefix
59
+ * const manager = new UpnpManager({
60
+ * config: UpnpConfig.fromEnvironment(),
61
+ * descriptionPrefix: 'My App',
62
+ * });
63
+ * // Mappings will be labelled "My App HTTP" and "My App WebSocket"
64
+ * ```
65
+ */
66
+ export declare class UpnpManager {
67
+ /** The underlying UPnP service */
68
+ private readonly service;
69
+ /** Server configuration */
70
+ private readonly config;
71
+ /** Prefix used in port mapping descriptions */
72
+ private readonly descriptionPrefix;
73
+ /** Periodic refresh timer handle */
74
+ private refreshTimer;
75
+ /** Consecutive refresh failure count (for exponential backoff) */
76
+ private consecutiveRefreshFailures;
77
+ /** Whether the manager has been initialized */
78
+ private initialized;
79
+ /** Whether shutdown is in progress or complete */
80
+ private shuttingDown;
81
+ /** Bound signal handlers (stored for removal on shutdown) */
82
+ private readonly boundSignalHandlers;
83
+ /**
84
+ * Create a new UpnpManager.
85
+ *
86
+ * Accepts either a config directly (backward compatible) or an options
87
+ * object with an optional `descriptionPrefix`.
88
+ *
89
+ * @param configOrOptions - UPnP configuration or options object
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * // Direct config (backward compatible)
94
+ * const manager = new UpnpManager(config);
95
+ *
96
+ * // Options object with custom prefix
97
+ * const manager = new UpnpManager({
98
+ * config,
99
+ * descriptionPrefix: 'My App',
100
+ * });
101
+ * ```
102
+ */
103
+ constructor(configOrOptions: IUpnpConfig | UpnpConfig | UpnpManagerOptions);
104
+ /**
105
+ * Initialize UPnP: create HTTP port mapping and start the refresh timer.
106
+ *
107
+ * **Validates: Requirements 4.4**
108
+ *
109
+ * On failure, logs a warning with manual port-forwarding instructions
110
+ * but does NOT throw — UPnP failure is non-fatal.
111
+ */
112
+ initialize(): Promise<void>;
113
+ /**
114
+ * Shut down UPnP: stop refresh timer, remove all mappings, close service.
115
+ *
116
+ * **Validates: Requirements 4.5, 4.6**
117
+ *
118
+ * Safe to call multiple times — subsequent calls are no-ops.
119
+ */
120
+ shutdown(): Promise<void>;
121
+ /**
122
+ * Whether the manager has been successfully initialized.
123
+ *
124
+ * @returns `true` if {@link initialize} completed successfully
125
+ */
126
+ get isInitialized(): boolean;
127
+ /**
128
+ * Whether the manager is shutting down or has shut down.
129
+ *
130
+ * @returns `true` if {@link shutdown} has been called
131
+ */
132
+ get isShuttingDown(): boolean;
133
+ /**
134
+ * Get the external endpoints for advertisement.
135
+ *
136
+ * Returns the external IP:port for HTTP and WebSocket endpoints.
137
+ *
138
+ * **Validates: Requirement 4.10**
139
+ *
140
+ * @returns External endpoints or `null` if UPnP is not initialized or shutting down
141
+ */
142
+ getExternalEndpoints(): Promise<{
143
+ http: string;
144
+ ws: string;
145
+ } | null>;
146
+ /**
147
+ * Create the HTTP port mapping on the router.
148
+ *
149
+ * **Validates: Requirement 4.2** — Uses configurable description prefix
150
+ */
151
+ private createHttpMapping;
152
+ /**
153
+ * Create the WebSocket port mapping on the router.
154
+ *
155
+ * Only called when websocketPort differs from httpPort.
156
+ *
157
+ * **Validates: Requirement 4.3** — Uses configurable description prefix
158
+ */
159
+ private createWebSocketMapping;
160
+ /**
161
+ * Whether the WebSocket port requires a separate mapping.
162
+ *
163
+ * @returns `true` if websocketPort differs from httpPort
164
+ */
165
+ private get needsWebSocketMapping();
166
+ /**
167
+ * Start the periodic refresh timer.
168
+ *
169
+ * **Validates: Requirement 4.8** — Refresh mappings before TTL expiration
170
+ */
171
+ private startRefreshTimer;
172
+ /**
173
+ * Stop the periodic refresh timer.
174
+ */
175
+ private stopRefreshTimer;
176
+ /**
177
+ * Refresh all active port mappings.
178
+ *
179
+ * **Validates: Requirement 4.8** — Re-create each active mapping to renew TTL
180
+ */
181
+ private refresh;
182
+ /**
183
+ * Schedule an additional refresh with exponential backoff.
184
+ *
185
+ * **Validates: Requirement 4.9** — Exponential backoff capped at 8× retryDelay
186
+ */
187
+ private scheduleBackoffRefresh;
188
+ /**
189
+ * Register SIGTERM and SIGINT handlers for graceful shutdown.
190
+ *
191
+ * **Validates: Requirement 4.4** — Register signal handlers during initialization
192
+ */
193
+ private registerSignalHandlers;
194
+ /**
195
+ * Remove previously registered signal handlers.
196
+ */
197
+ private removeSignalHandlers;
198
+ /**
199
+ * Handle a process signal by performing graceful shutdown.
200
+ *
201
+ * @param signal - The signal name (e.g. `'SIGTERM'`, `'SIGINT'`)
202
+ */
203
+ private handleSignal;
204
+ /**
205
+ * Log manual port-forwarding instructions when UPnP is unavailable.
206
+ *
207
+ * **Validates: Requirement 4.7** — Log warning with manual instructions
208
+ */
209
+ private logManualPortForwardingInstructions;
210
+ }
211
+ //# sourceMappingURL=upnp-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upnp-manager.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/upnp-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,WAAW,EAGZ,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAe3C;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,MAAM,EAAE,WAAW,GAAG,UAAU,CAAC;IACjC,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,WAAW;IACtB,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IAEtC,2BAA2B;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAE/C,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,oCAAoC;IACpC,OAAO,CAAC,YAAY,CAA+C;IAEnE,kEAAkE;IAClE,OAAO,CAAC,0BAA0B,CAAK;IAEvC,+CAA+C;IAC/C,OAAO,CAAC,WAAW,CAAS;IAE5B,kDAAkD;IAClD,OAAO,CAAC,YAAY,CAAS;IAE7B,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAGlC;IAEF;;;;;;;;;;;;;;;;;;;OAmBG;gBACS,eAAe,EAAE,WAAW,GAAG,UAAU,GAAG,kBAAkB;IAkC1E;;;;;;;OAOG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoDjC;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB/B;;;;OAIG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;;;OAIG;IACH,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;;;;;;;OAQG;IACG,oBAAoB,IAAI,OAAO,CAAC;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;KACZ,GAAG,IAAI,CAAC;IAkBT;;;;OAIG;YACW,iBAAiB;IAY/B;;;;;;OAMG;YACW,sBAAsB;IAYpC;;;;OAIG;IACH,OAAO,KAAK,qBAAqB,GAEhC;IAID;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;;OAIG;YACW,OAAO;IAsErB;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAwB9B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAK9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;;;OAIG;YACW,YAAY;IAO1B;;;;OAIG;IACH,OAAO,CAAC,mCAAmC;CAgB5C"}
@@ -0,0 +1,447 @@
1
+ "use strict";
2
+ /**
3
+ * UPnP Manager for Express server integration.
4
+ *
5
+ * Orchestrates UPnP port mapping lifecycle: initialization, periodic refresh,
6
+ * health monitoring, and graceful shutdown. Wraps the core UpnpService with
7
+ * server-specific concerns like signal handling and exponential backoff on
8
+ * refresh failures.
9
+ *
10
+ * All UPnP failures are non-fatal — the server continues operating even if
11
+ * port mapping fails, with appropriate log messages for manual intervention.
12
+ *
13
+ * Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 4.10
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.UpnpManager = void 0;
17
+ const upnp_1 = require("./upnp");
18
+ // ─── Constants ──────────────────────────────────────────────────────────────
19
+ /** Maximum backoff multiplier for repeated refresh failures */
20
+ const MAX_BACKOFF_MULTIPLIER = 8;
21
+ /** Prefix for all UPnP log messages */
22
+ const LOG_PREFIX = '[UPnP]';
23
+ /** Default description prefix for port mapping labels */
24
+ const DEFAULT_DESCRIPTION_PREFIX = 'Express App';
25
+ // ─── Type Guard ─────────────────────────────────────────────────────────────
26
+ /**
27
+ * Check whether the given value is a UpnpManagerOptions object
28
+ * (as opposed to a plain IUpnpConfig or UpnpConfig).
29
+ */
30
+ function isUpnpManagerOptions(value) {
31
+ return (typeof value === 'object' &&
32
+ value !== null &&
33
+ 'config' in value &&
34
+ // UpnpConfig and IUpnpConfig both have 'enabled'; UpnpManagerOptions has 'config'
35
+ typeof value.config === 'object');
36
+ }
37
+ // ─── Manager ────────────────────────────────────────────────────────────────
38
+ /**
39
+ * Manages UPnP port mappings for the Express server lifecycle.
40
+ *
41
+ * Handles:
42
+ * - Creating HTTP port mapping on startup
43
+ * - Creating WebSocket port mapping when on a separate port
44
+ * - Removing mappings on shutdown
45
+ * - Periodic health monitoring and refresh
46
+ * - SIGTERM/SIGINT signal handling
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const config = UpnpConfig.fromEnvironment();
51
+ * const manager = new UpnpManager(config);
52
+ *
53
+ * await manager.initialize(); // creates mapping, starts refresh timer
54
+ * // ... server runs ...
55
+ * await manager.shutdown(); // removes mappings, cleans up
56
+ * ```
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // With custom description prefix
61
+ * const manager = new UpnpManager({
62
+ * config: UpnpConfig.fromEnvironment(),
63
+ * descriptionPrefix: 'My App',
64
+ * });
65
+ * // Mappings will be labelled "My App HTTP" and "My App WebSocket"
66
+ * ```
67
+ */
68
+ class UpnpManager {
69
+ /** The underlying UPnP service */
70
+ service;
71
+ /** Server configuration */
72
+ config;
73
+ /** Prefix used in port mapping descriptions */
74
+ descriptionPrefix;
75
+ /** Periodic refresh timer handle */
76
+ refreshTimer = null;
77
+ /** Consecutive refresh failure count (for exponential backoff) */
78
+ consecutiveRefreshFailures = 0;
79
+ /** Whether the manager has been initialized */
80
+ initialized = false;
81
+ /** Whether shutdown is in progress or complete */
82
+ shuttingDown = false;
83
+ /** Bound signal handlers (stored for removal on shutdown) */
84
+ boundSignalHandlers;
85
+ /**
86
+ * Create a new UpnpManager.
87
+ *
88
+ * Accepts either a config directly (backward compatible) or an options
89
+ * object with an optional `descriptionPrefix`.
90
+ *
91
+ * @param configOrOptions - UPnP configuration or options object
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * // Direct config (backward compatible)
96
+ * const manager = new UpnpManager(config);
97
+ *
98
+ * // Options object with custom prefix
99
+ * const manager = new UpnpManager({
100
+ * config,
101
+ * descriptionPrefix: 'My App',
102
+ * });
103
+ * ```
104
+ */
105
+ constructor(configOrOptions) {
106
+ let resolvedConfig;
107
+ let prefix;
108
+ if (isUpnpManagerOptions(configOrOptions)) {
109
+ resolvedConfig = configOrOptions.config;
110
+ prefix = configOrOptions.descriptionPrefix ?? DEFAULT_DESCRIPTION_PREFIX;
111
+ }
112
+ else {
113
+ resolvedConfig = configOrOptions;
114
+ prefix = DEFAULT_DESCRIPTION_PREFIX;
115
+ }
116
+ this.config = resolvedConfig;
117
+ this.descriptionPrefix = prefix;
118
+ this.service = new upnp_1.UpnpService({
119
+ enabled: resolvedConfig.enabled,
120
+ httpPort: resolvedConfig.httpPort,
121
+ websocketPort: resolvedConfig.websocketPort,
122
+ ttl: resolvedConfig.ttl,
123
+ refreshInterval: resolvedConfig.refreshInterval,
124
+ protocol: resolvedConfig.protocol,
125
+ retryAttempts: resolvedConfig.retryAttempts,
126
+ retryDelay: resolvedConfig.retryDelay,
127
+ });
128
+ // Bind signal handlers so we can remove them later
129
+ this.boundSignalHandlers = {
130
+ sigterm: () => void this.handleSignal('SIGTERM'),
131
+ sigint: () => void this.handleSignal('SIGINT'),
132
+ };
133
+ }
134
+ // ─── Public API ─────────────────────────────────────────────────────────
135
+ /**
136
+ * Initialize UPnP: create HTTP port mapping and start the refresh timer.
137
+ *
138
+ * **Validates: Requirements 4.4**
139
+ *
140
+ * On failure, logs a warning with manual port-forwarding instructions
141
+ * but does NOT throw — UPnP failure is non-fatal.
142
+ */
143
+ async initialize() {
144
+ if (this.initialized) {
145
+ console.warn(`${LOG_PREFIX} Already initialized, skipping`);
146
+ return;
147
+ }
148
+ console.log(`${LOG_PREFIX} Initializing UPnP port mapping...`);
149
+ // Register signal handlers for graceful shutdown
150
+ this.registerSignalHandlers();
151
+ try {
152
+ // Discover external IP
153
+ const externalIp = await this.service.getExternalIp();
154
+ console.log(`${LOG_PREFIX} External IP: ${externalIp}`);
155
+ // Create HTTP port mapping
156
+ await this.createHttpMapping();
157
+ console.log(`${LOG_PREFIX} HTTP port mapping created — ` +
158
+ `external ${externalIp}:${this.config.httpPort} → ` +
159
+ `internal :${this.config.httpPort}`);
160
+ // Create WebSocket port mapping if on a different port
161
+ if (this.needsWebSocketMapping) {
162
+ await this.createWebSocketMapping();
163
+ console.log(`${LOG_PREFIX} WebSocket port mapping created — ` +
164
+ `external ${externalIp}:${this.config.websocketPort} → ` +
165
+ `internal :${this.config.websocketPort}`);
166
+ }
167
+ else {
168
+ console.log(`${LOG_PREFIX} WebSocket using same port as HTTP (${this.config.httpPort}), no additional mapping needed`);
169
+ }
170
+ // Start periodic refresh timer
171
+ this.startRefreshTimer();
172
+ this.initialized = true;
173
+ console.log(`${LOG_PREFIX} Initialization complete`);
174
+ }
175
+ catch (error) {
176
+ const message = error instanceof Error ? error.message : String(error);
177
+ console.warn(`${LOG_PREFIX} Initialization failed: ${message}`);
178
+ this.logManualPortForwardingInstructions();
179
+ // Non-fatal: server continues without UPnP
180
+ }
181
+ }
182
+ /**
183
+ * Shut down UPnP: stop refresh timer, remove all mappings, close service.
184
+ *
185
+ * **Validates: Requirements 4.5, 4.6**
186
+ *
187
+ * Safe to call multiple times — subsequent calls are no-ops.
188
+ */
189
+ async shutdown() {
190
+ if (this.shuttingDown) {
191
+ return;
192
+ }
193
+ this.shuttingDown = true;
194
+ console.log(`${LOG_PREFIX} Shutting down...`);
195
+ // Stop refresh timer
196
+ this.stopRefreshTimer();
197
+ // Remove signal handlers
198
+ this.removeSignalHandlers();
199
+ // Remove all mappings and close the service
200
+ try {
201
+ await this.service.close();
202
+ console.log(`${LOG_PREFIX} All mappings removed and service closed`);
203
+ }
204
+ catch (error) {
205
+ const message = error instanceof Error ? error.message : String(error);
206
+ console.error(`${LOG_PREFIX} Error during shutdown: ${message}`);
207
+ }
208
+ }
209
+ /**
210
+ * Whether the manager has been successfully initialized.
211
+ *
212
+ * @returns `true` if {@link initialize} completed successfully
213
+ */
214
+ get isInitialized() {
215
+ return this.initialized;
216
+ }
217
+ /**
218
+ * Whether the manager is shutting down or has shut down.
219
+ *
220
+ * @returns `true` if {@link shutdown} has been called
221
+ */
222
+ get isShuttingDown() {
223
+ return this.shuttingDown;
224
+ }
225
+ /**
226
+ * Get the external endpoints for advertisement.
227
+ *
228
+ * Returns the external IP:port for HTTP and WebSocket endpoints.
229
+ *
230
+ * **Validates: Requirement 4.10**
231
+ *
232
+ * @returns External endpoints or `null` if UPnP is not initialized or shutting down
233
+ */
234
+ async getExternalEndpoints() {
235
+ if (!this.initialized || this.shuttingDown) {
236
+ return null;
237
+ }
238
+ try {
239
+ const externalIp = await this.service.getExternalIp();
240
+ return {
241
+ http: `http://${externalIp}:${this.config.httpPort}`,
242
+ ws: `ws://${externalIp}:${this.config.websocketPort}`,
243
+ };
244
+ }
245
+ catch {
246
+ return null;
247
+ }
248
+ }
249
+ // ─── Private: Port Mapping ──────────────────────────────────────────────
250
+ /**
251
+ * Create the HTTP port mapping on the router.
252
+ *
253
+ * **Validates: Requirement 4.2** — Uses configurable description prefix
254
+ */
255
+ async createHttpMapping() {
256
+ const mapping = {
257
+ public: this.config.httpPort,
258
+ private: this.config.httpPort,
259
+ protocol: 'tcp',
260
+ description: `${this.descriptionPrefix} HTTP`,
261
+ ttl: this.config.ttl,
262
+ };
263
+ await this.service.createPortMapping(mapping);
264
+ }
265
+ /**
266
+ * Create the WebSocket port mapping on the router.
267
+ *
268
+ * Only called when websocketPort differs from httpPort.
269
+ *
270
+ * **Validates: Requirement 4.3** — Uses configurable description prefix
271
+ */
272
+ async createWebSocketMapping() {
273
+ const mapping = {
274
+ public: this.config.websocketPort,
275
+ private: this.config.websocketPort,
276
+ protocol: 'tcp',
277
+ description: `${this.descriptionPrefix} WebSocket`,
278
+ ttl: this.config.ttl,
279
+ };
280
+ await this.service.createPortMapping(mapping);
281
+ }
282
+ /**
283
+ * Whether the WebSocket port requires a separate mapping.
284
+ *
285
+ * @returns `true` if websocketPort differs from httpPort
286
+ */
287
+ get needsWebSocketMapping() {
288
+ return this.config.websocketPort !== this.config.httpPort;
289
+ }
290
+ // ─── Private: Refresh ───────────────────────────────────────────────────
291
+ /**
292
+ * Start the periodic refresh timer.
293
+ *
294
+ * **Validates: Requirement 4.8** — Refresh mappings before TTL expiration
295
+ */
296
+ startRefreshTimer() {
297
+ if (this.refreshTimer) {
298
+ return;
299
+ }
300
+ const intervalMs = this.config.refreshInterval;
301
+ console.log(`${LOG_PREFIX} Refresh timer started (interval: ${intervalMs}ms)`);
302
+ this.refreshTimer = setInterval(() => {
303
+ void this.refresh();
304
+ }, intervalMs);
305
+ // Allow the process to exit even if the timer is still running
306
+ if (this.refreshTimer &&
307
+ typeof this.refreshTimer === 'object' &&
308
+ 'unref' in this.refreshTimer) {
309
+ this.refreshTimer.unref();
310
+ }
311
+ }
312
+ /**
313
+ * Stop the periodic refresh timer.
314
+ */
315
+ stopRefreshTimer() {
316
+ if (this.refreshTimer) {
317
+ clearInterval(this.refreshTimer);
318
+ this.refreshTimer = null;
319
+ console.log(`${LOG_PREFIX} Refresh timer stopped`);
320
+ }
321
+ }
322
+ /**
323
+ * Refresh all active port mappings.
324
+ *
325
+ * **Validates: Requirement 4.8** — Re-create each active mapping to renew TTL
326
+ */
327
+ async refresh() {
328
+ if (this.shuttingDown) {
329
+ return;
330
+ }
331
+ try {
332
+ // Get current active mappings from the service
333
+ const activeMappings = await this.service.getMappings();
334
+ if (activeMappings.length === 0) {
335
+ // No active mappings — try to recreate
336
+ console.warn(`${LOG_PREFIX} No active mappings found during refresh, recreating...`);
337
+ await this.createHttpMapping();
338
+ if (this.needsWebSocketMapping) {
339
+ await this.createWebSocketMapping();
340
+ }
341
+ this.consecutiveRefreshFailures = 0;
342
+ console.log(`${LOG_PREFIX} Mapping(s) recreated successfully`);
343
+ return;
344
+ }
345
+ // Refresh each active mapping by re-creating it
346
+ const errors = [];
347
+ for (const mapping of activeMappings) {
348
+ try {
349
+ await this.service.createPortMapping(mapping);
350
+ }
351
+ catch (error) {
352
+ const msg = error instanceof Error ? error.message : String(error);
353
+ errors.push(`${mapping.public}/${mapping.protocol}: ${msg}`);
354
+ }
355
+ }
356
+ if (errors.length > 0) {
357
+ console.warn(`${LOG_PREFIX} Partial refresh failure: ${errors.join('; ')}`);
358
+ this.consecutiveRefreshFailures++;
359
+ this.scheduleBackoffRefresh();
360
+ return;
361
+ }
362
+ // Verify mappings still exist after refresh
363
+ const verifiedMappings = await this.service.getMappings();
364
+ if (verifiedMappings.length < activeMappings.length) {
365
+ console.warn(`${LOG_PREFIX} Mapping verification failed — ` +
366
+ `expected ${activeMappings.length}, found ${verifiedMappings.length}. Recreating...`);
367
+ await this.createHttpMapping();
368
+ if (this.needsWebSocketMapping) {
369
+ await this.createWebSocketMapping();
370
+ }
371
+ }
372
+ this.consecutiveRefreshFailures = 0;
373
+ console.log(`${LOG_PREFIX} Refresh complete (${verifiedMappings.length} mapping(s) active)`);
374
+ }
375
+ catch (error) {
376
+ this.consecutiveRefreshFailures++;
377
+ const message = error instanceof Error ? error.message : String(error);
378
+ console.error(`${LOG_PREFIX} Refresh failed (attempt ${this.consecutiveRefreshFailures}): ${message}`);
379
+ this.scheduleBackoffRefresh();
380
+ }
381
+ }
382
+ /**
383
+ * Schedule an additional refresh with exponential backoff.
384
+ *
385
+ * **Validates: Requirement 4.9** — Exponential backoff capped at 8× retryDelay
386
+ */
387
+ scheduleBackoffRefresh() {
388
+ const multiplier = Math.min(Math.pow(2, this.consecutiveRefreshFailures - 1), MAX_BACKOFF_MULTIPLIER);
389
+ const backoffMs = this.config.retryDelay * multiplier;
390
+ console.warn(`${LOG_PREFIX} Scheduling backoff refresh in ${backoffMs}ms ` +
391
+ `(failure #${this.consecutiveRefreshFailures})`);
392
+ const timer = setTimeout(() => {
393
+ void this.refresh();
394
+ }, backoffMs);
395
+ // Don't prevent process exit
396
+ if (timer && typeof timer === 'object' && 'unref' in timer) {
397
+ timer.unref();
398
+ }
399
+ }
400
+ // ─── Private: Signal Handling ───────────────────────────────────────────
401
+ /**
402
+ * Register SIGTERM and SIGINT handlers for graceful shutdown.
403
+ *
404
+ * **Validates: Requirement 4.4** — Register signal handlers during initialization
405
+ */
406
+ registerSignalHandlers() {
407
+ process.on('SIGTERM', this.boundSignalHandlers.sigterm);
408
+ process.on('SIGINT', this.boundSignalHandlers.sigint);
409
+ }
410
+ /**
411
+ * Remove previously registered signal handlers.
412
+ */
413
+ removeSignalHandlers() {
414
+ process.removeListener('SIGTERM', this.boundSignalHandlers.sigterm);
415
+ process.removeListener('SIGINT', this.boundSignalHandlers.sigint);
416
+ }
417
+ /**
418
+ * Handle a process signal by performing graceful shutdown.
419
+ *
420
+ * @param signal - The signal name (e.g. `'SIGTERM'`, `'SIGINT'`)
421
+ */
422
+ async handleSignal(signal) {
423
+ console.log(`${LOG_PREFIX} Received ${signal}, shutting down UPnP...`);
424
+ await this.shutdown();
425
+ }
426
+ // ─── Private: Logging Helpers ───────────────────────────────────────────
427
+ /**
428
+ * Log manual port-forwarding instructions when UPnP is unavailable.
429
+ *
430
+ * **Validates: Requirement 4.7** — Log warning with manual instructions
431
+ */
432
+ logManualPortForwardingInstructions() {
433
+ let instructions = `${LOG_PREFIX} UPnP not available. Manual port forwarding required:\n` +
434
+ ` Forward external port ${this.config.httpPort} to internal port ${this.config.httpPort}\n` +
435
+ ` Protocol: TCP\n` +
436
+ ` Description: ${this.descriptionPrefix} HTTP`;
437
+ if (this.needsWebSocketMapping) {
438
+ instructions +=
439
+ `\n Forward external port ${this.config.websocketPort} to internal port ${this.config.websocketPort}\n` +
440
+ ` Protocol: TCP\n` +
441
+ ` Description: ${this.descriptionPrefix} WebSocket`;
442
+ }
443
+ console.warn(instructions);
444
+ }
445
+ }
446
+ exports.UpnpManager = UpnpManager;
447
+ //# sourceMappingURL=upnp-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upnp-manager.js","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-node-express-suite/src/services/upnp-manager.ts"],"names":[],"mappings":"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"}