@push.rocks/smartproxy 19.5.4 → 19.5.5

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/dist_ts/core/utils/async-utils.d.ts +81 -0
  2. package/dist_ts/core/utils/async-utils.js +216 -0
  3. package/dist_ts/core/utils/binary-heap.d.ts +73 -0
  4. package/dist_ts/core/utils/binary-heap.js +193 -0
  5. package/dist_ts/core/utils/enhanced-connection-pool.d.ts +110 -0
  6. package/dist_ts/core/utils/enhanced-connection-pool.js +320 -0
  7. package/dist_ts/core/utils/fs-utils.d.ts +144 -0
  8. package/dist_ts/core/utils/fs-utils.js +252 -0
  9. package/dist_ts/core/utils/index.d.ts +5 -2
  10. package/dist_ts/core/utils/index.js +6 -3
  11. package/dist_ts/core/utils/lifecycle-component.d.ts +59 -0
  12. package/dist_ts/core/utils/lifecycle-component.js +195 -0
  13. package/dist_ts/plugins.d.ts +2 -1
  14. package/dist_ts/plugins.js +3 -2
  15. package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +15 -0
  16. package/dist_ts/proxies/http-proxy/certificate-manager.js +49 -2
  17. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +10 -0
  18. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +53 -43
  19. package/dist_ts/proxies/smart-proxy/cert-store.js +22 -20
  20. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +37 -7
  21. package/dist_ts/proxies/smart-proxy/connection-manager.js +257 -180
  22. package/package.json +2 -2
  23. package/readme.hints.md +96 -1
  24. package/readme.plan.md +1135 -221
  25. package/readme.problems.md +167 -83
  26. package/ts/core/utils/async-utils.ts +275 -0
  27. package/ts/core/utils/binary-heap.ts +225 -0
  28. package/ts/core/utils/enhanced-connection-pool.ts +420 -0
  29. package/ts/core/utils/fs-utils.ts +270 -0
  30. package/ts/core/utils/index.ts +5 -2
  31. package/ts/core/utils/lifecycle-component.ts +231 -0
  32. package/ts/plugins.ts +2 -1
  33. package/ts/proxies/http-proxy/certificate-manager.ts +52 -1
  34. package/ts/proxies/nftables-proxy/nftables-proxy.ts +64 -79
  35. package/ts/proxies/smart-proxy/cert-store.ts +26 -20
  36. package/ts/proxies/smart-proxy/connection-manager.ts +291 -189
  37. package/readme.plan2.md +0 -764
  38. package/ts/common/eventUtils.ts +0 -34
  39. package/ts/common/types.ts +0 -91
  40. package/ts/core/utils/event-system.ts +0 -376
  41. package/ts/core/utils/event-utils.ts +0 -25
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Async filesystem utilities for SmartProxy
3
+ * Provides non-blocking alternatives to synchronous filesystem operations
4
+ */
5
+
6
+ import * as plugins from '../../plugins.js';
7
+
8
+ export class AsyncFileSystem {
9
+ /**
10
+ * Check if a file or directory exists
11
+ * @param path - Path to check
12
+ * @returns Promise resolving to true if exists, false otherwise
13
+ */
14
+ static async exists(path: string): Promise<boolean> {
15
+ try {
16
+ await plugins.fs.promises.access(path);
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Ensure a directory exists, creating it if necessary
25
+ * @param dirPath - Directory path to ensure
26
+ * @returns Promise that resolves when directory is ensured
27
+ */
28
+ static async ensureDir(dirPath: string): Promise<void> {
29
+ await plugins.fs.promises.mkdir(dirPath, { recursive: true });
30
+ }
31
+
32
+ /**
33
+ * Read a file as string
34
+ * @param filePath - Path to the file
35
+ * @param encoding - File encoding (default: utf8)
36
+ * @returns Promise resolving to file contents
37
+ */
38
+ static async readFile(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
39
+ return plugins.fs.promises.readFile(filePath, encoding);
40
+ }
41
+
42
+ /**
43
+ * Read a file as buffer
44
+ * @param filePath - Path to the file
45
+ * @returns Promise resolving to file buffer
46
+ */
47
+ static async readFileBuffer(filePath: string): Promise<Buffer> {
48
+ return plugins.fs.promises.readFile(filePath);
49
+ }
50
+
51
+ /**
52
+ * Write string data to a file
53
+ * @param filePath - Path to the file
54
+ * @param data - String data to write
55
+ * @param encoding - File encoding (default: utf8)
56
+ * @returns Promise that resolves when file is written
57
+ */
58
+ static async writeFile(filePath: string, data: string, encoding: BufferEncoding = 'utf8'): Promise<void> {
59
+ // Ensure directory exists
60
+ const dir = plugins.path.dirname(filePath);
61
+ await this.ensureDir(dir);
62
+ await plugins.fs.promises.writeFile(filePath, data, encoding);
63
+ }
64
+
65
+ /**
66
+ * Write buffer data to a file
67
+ * @param filePath - Path to the file
68
+ * @param data - Buffer data to write
69
+ * @returns Promise that resolves when file is written
70
+ */
71
+ static async writeFileBuffer(filePath: string, data: Buffer): Promise<void> {
72
+ const dir = plugins.path.dirname(filePath);
73
+ await this.ensureDir(dir);
74
+ await plugins.fs.promises.writeFile(filePath, data);
75
+ }
76
+
77
+ /**
78
+ * Remove a file
79
+ * @param filePath - Path to the file
80
+ * @returns Promise that resolves when file is removed
81
+ */
82
+ static async remove(filePath: string): Promise<void> {
83
+ try {
84
+ await plugins.fs.promises.unlink(filePath);
85
+ } catch (error: any) {
86
+ if (error.code !== 'ENOENT') {
87
+ throw error;
88
+ }
89
+ // File doesn't exist, which is fine
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Remove a directory and all its contents
95
+ * @param dirPath - Path to the directory
96
+ * @returns Promise that resolves when directory is removed
97
+ */
98
+ static async removeDir(dirPath: string): Promise<void> {
99
+ try {
100
+ await plugins.fs.promises.rm(dirPath, { recursive: true, force: true });
101
+ } catch (error: any) {
102
+ if (error.code !== 'ENOENT') {
103
+ throw error;
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Read JSON from a file
110
+ * @param filePath - Path to the JSON file
111
+ * @returns Promise resolving to parsed JSON
112
+ */
113
+ static async readJSON<T = any>(filePath: string): Promise<T> {
114
+ const content = await this.readFile(filePath);
115
+ return JSON.parse(content);
116
+ }
117
+
118
+ /**
119
+ * Write JSON to a file
120
+ * @param filePath - Path to the file
121
+ * @param data - Data to write as JSON
122
+ * @param pretty - Whether to pretty-print JSON (default: true)
123
+ * @returns Promise that resolves when file is written
124
+ */
125
+ static async writeJSON(filePath: string, data: any, pretty = true): Promise<void> {
126
+ const jsonString = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
127
+ await this.writeFile(filePath, jsonString);
128
+ }
129
+
130
+ /**
131
+ * Copy a file from source to destination
132
+ * @param source - Source file path
133
+ * @param destination - Destination file path
134
+ * @returns Promise that resolves when file is copied
135
+ */
136
+ static async copyFile(source: string, destination: string): Promise<void> {
137
+ const destDir = plugins.path.dirname(destination);
138
+ await this.ensureDir(destDir);
139
+ await plugins.fs.promises.copyFile(source, destination);
140
+ }
141
+
142
+ /**
143
+ * Move/rename a file
144
+ * @param source - Source file path
145
+ * @param destination - Destination file path
146
+ * @returns Promise that resolves when file is moved
147
+ */
148
+ static async moveFile(source: string, destination: string): Promise<void> {
149
+ const destDir = plugins.path.dirname(destination);
150
+ await this.ensureDir(destDir);
151
+ await plugins.fs.promises.rename(source, destination);
152
+ }
153
+
154
+ /**
155
+ * Get file stats
156
+ * @param filePath - Path to the file
157
+ * @returns Promise resolving to file stats or null if doesn't exist
158
+ */
159
+ static async getStats(filePath: string): Promise<plugins.fs.Stats | null> {
160
+ try {
161
+ return await plugins.fs.promises.stat(filePath);
162
+ } catch (error: any) {
163
+ if (error.code === 'ENOENT') {
164
+ return null;
165
+ }
166
+ throw error;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * List files in a directory
172
+ * @param dirPath - Directory path
173
+ * @returns Promise resolving to array of filenames
174
+ */
175
+ static async listFiles(dirPath: string): Promise<string[]> {
176
+ try {
177
+ return await plugins.fs.promises.readdir(dirPath);
178
+ } catch (error: any) {
179
+ if (error.code === 'ENOENT') {
180
+ return [];
181
+ }
182
+ throw error;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * List files in a directory with full paths
188
+ * @param dirPath - Directory path
189
+ * @returns Promise resolving to array of full file paths
190
+ */
191
+ static async listFilesFullPath(dirPath: string): Promise<string[]> {
192
+ const files = await this.listFiles(dirPath);
193
+ return files.map(file => plugins.path.join(dirPath, file));
194
+ }
195
+
196
+ /**
197
+ * Recursively list all files in a directory
198
+ * @param dirPath - Directory path
199
+ * @param fileList - Accumulator for file list (used internally)
200
+ * @returns Promise resolving to array of all file paths
201
+ */
202
+ static async listFilesRecursive(dirPath: string, fileList: string[] = []): Promise<string[]> {
203
+ const files = await this.listFiles(dirPath);
204
+
205
+ for (const file of files) {
206
+ const filePath = plugins.path.join(dirPath, file);
207
+ const stats = await this.getStats(filePath);
208
+
209
+ if (stats?.isDirectory()) {
210
+ await this.listFilesRecursive(filePath, fileList);
211
+ } else if (stats?.isFile()) {
212
+ fileList.push(filePath);
213
+ }
214
+ }
215
+
216
+ return fileList;
217
+ }
218
+
219
+ /**
220
+ * Create a read stream for a file
221
+ * @param filePath - Path to the file
222
+ * @param options - Stream options
223
+ * @returns Read stream
224
+ */
225
+ static createReadStream(filePath: string, options?: Parameters<typeof plugins.fs.createReadStream>[1]): plugins.fs.ReadStream {
226
+ return plugins.fs.createReadStream(filePath, options);
227
+ }
228
+
229
+ /**
230
+ * Create a write stream for a file
231
+ * @param filePath - Path to the file
232
+ * @param options - Stream options
233
+ * @returns Write stream
234
+ */
235
+ static createWriteStream(filePath: string, options?: Parameters<typeof plugins.fs.createWriteStream>[1]): plugins.fs.WriteStream {
236
+ return plugins.fs.createWriteStream(filePath, options);
237
+ }
238
+
239
+ /**
240
+ * Ensure a file exists, creating an empty file if necessary
241
+ * @param filePath - Path to the file
242
+ * @returns Promise that resolves when file is ensured
243
+ */
244
+ static async ensureFile(filePath: string): Promise<void> {
245
+ const exists = await this.exists(filePath);
246
+ if (!exists) {
247
+ await this.writeFile(filePath, '');
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Check if a path is a directory
253
+ * @param path - Path to check
254
+ * @returns Promise resolving to true if directory, false otherwise
255
+ */
256
+ static async isDirectory(path: string): Promise<boolean> {
257
+ const stats = await this.getStats(path);
258
+ return stats?.isDirectory() ?? false;
259
+ }
260
+
261
+ /**
262
+ * Check if a path is a file
263
+ * @param path - Path to check
264
+ * @returns Promise resolving to true if file, false otherwise
265
+ */
266
+ static async isFile(path: string): Promise<boolean> {
267
+ const stats = await this.getStats(path);
268
+ return stats?.isFile() ?? false;
269
+ }
270
+ }
@@ -2,7 +2,6 @@
2
2
  * Core utility functions
3
3
  */
4
4
 
5
- export * from './event-utils.js';
6
5
  export * from './validation-utils.js';
7
6
  export * from './ip-utils.js';
8
7
  export * from './template-utils.js';
@@ -10,6 +9,10 @@ export * from './route-manager.js';
10
9
  export * from './route-utils.js';
11
10
  export * from './security-utils.js';
12
11
  export * from './shared-security-manager.js';
13
- export * from './event-system.js';
14
12
  export * from './websocket-utils.js';
15
13
  export * from './logger.js';
14
+ export * from './async-utils.js';
15
+ export * from './fs-utils.js';
16
+ export * from './lifecycle-component.js';
17
+ export * from './binary-heap.js';
18
+ export * from './enhanced-connection-pool.js';
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Base class for components that need proper resource lifecycle management
3
+ * Provides automatic cleanup of timers and event listeners to prevent memory leaks
4
+ */
5
+ export abstract class LifecycleComponent {
6
+ private timers: Set<NodeJS.Timeout> = new Set();
7
+ private intervals: Set<NodeJS.Timeout> = new Set();
8
+ private listeners: Array<{
9
+ target: any;
10
+ event: string;
11
+ handler: Function;
12
+ once?: boolean;
13
+ }> = [];
14
+ private childComponents: Set<LifecycleComponent> = new Set();
15
+ protected isShuttingDown = false;
16
+ private cleanupPromise?: Promise<void>;
17
+
18
+ /**
19
+ * Create a managed setTimeout that will be automatically cleaned up
20
+ */
21
+ protected setTimeout(handler: Function, timeout: number): NodeJS.Timeout {
22
+ if (this.isShuttingDown) {
23
+ // Return a dummy timer if shutting down
24
+ return setTimeout(() => {}, 0);
25
+ }
26
+
27
+ const wrappedHandler = () => {
28
+ this.timers.delete(timer);
29
+ if (!this.isShuttingDown) {
30
+ handler();
31
+ }
32
+ };
33
+
34
+ const timer = setTimeout(wrappedHandler, timeout);
35
+ this.timers.add(timer);
36
+ return timer;
37
+ }
38
+
39
+ /**
40
+ * Create a managed setInterval that will be automatically cleaned up
41
+ */
42
+ protected setInterval(handler: Function, interval: number): NodeJS.Timeout {
43
+ if (this.isShuttingDown) {
44
+ // Return a dummy timer if shutting down
45
+ return setInterval(() => {}, interval);
46
+ }
47
+
48
+ const wrappedHandler = () => {
49
+ if (!this.isShuttingDown) {
50
+ handler();
51
+ }
52
+ };
53
+
54
+ const timer = setInterval(wrappedHandler, interval);
55
+ this.intervals.add(timer);
56
+
57
+ // Allow process to exit even with timer
58
+ if (typeof timer.unref === 'function') {
59
+ timer.unref();
60
+ }
61
+
62
+ return timer;
63
+ }
64
+
65
+ /**
66
+ * Clear a managed timeout
67
+ */
68
+ protected clearTimeout(timer: NodeJS.Timeout): void {
69
+ clearTimeout(timer);
70
+ this.timers.delete(timer);
71
+ }
72
+
73
+ /**
74
+ * Clear a managed interval
75
+ */
76
+ protected clearInterval(timer: NodeJS.Timeout): void {
77
+ clearInterval(timer);
78
+ this.intervals.delete(timer);
79
+ }
80
+
81
+ /**
82
+ * Add a managed event listener that will be automatically removed on cleanup
83
+ */
84
+ protected addEventListener(
85
+ target: any,
86
+ event: string,
87
+ handler: Function,
88
+ options?: { once?: boolean }
89
+ ): void {
90
+ if (this.isShuttingDown) {
91
+ return;
92
+ }
93
+
94
+ // For 'once' listeners, we need to wrap the handler to remove it from our tracking
95
+ let actualHandler = handler;
96
+ if (options?.once) {
97
+ actualHandler = (...args: any[]) => {
98
+ // Call the original handler
99
+ handler(...args);
100
+
101
+ // Remove from our internal tracking
102
+ const index = this.listeners.findIndex(
103
+ l => l.target === target && l.event === event && l.handler === handler
104
+ );
105
+ if (index !== -1) {
106
+ this.listeners.splice(index, 1);
107
+ }
108
+ };
109
+ }
110
+
111
+ // Support both EventEmitter and DOM-style event targets
112
+ if (typeof target.on === 'function') {
113
+ if (options?.once) {
114
+ target.once(event, actualHandler);
115
+ } else {
116
+ target.on(event, actualHandler);
117
+ }
118
+ } else if (typeof target.addEventListener === 'function') {
119
+ target.addEventListener(event, actualHandler, options);
120
+ } else {
121
+ throw new Error('Target must support on() or addEventListener()');
122
+ }
123
+
124
+ // Store the original handler in our tracking (not the wrapped one)
125
+ this.listeners.push({
126
+ target,
127
+ event,
128
+ handler,
129
+ once: options?.once
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Remove a specific event listener
135
+ */
136
+ protected removeEventListener(target: any, event: string, handler: Function): void {
137
+ // Remove from target
138
+ if (typeof target.removeListener === 'function') {
139
+ target.removeListener(event, handler);
140
+ } else if (typeof target.removeEventListener === 'function') {
141
+ target.removeEventListener(event, handler);
142
+ }
143
+
144
+ // Remove from our tracking
145
+ const index = this.listeners.findIndex(
146
+ l => l.target === target && l.event === event && l.handler === handler
147
+ );
148
+ if (index !== -1) {
149
+ this.listeners.splice(index, 1);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Register a child component that should be cleaned up when this component is cleaned up
155
+ */
156
+ protected registerChildComponent(component: LifecycleComponent): void {
157
+ this.childComponents.add(component);
158
+ }
159
+
160
+ /**
161
+ * Unregister a child component
162
+ */
163
+ protected unregisterChildComponent(component: LifecycleComponent): void {
164
+ this.childComponents.delete(component);
165
+ }
166
+
167
+ /**
168
+ * Override this method to implement component-specific cleanup logic
169
+ */
170
+ protected async onCleanup(): Promise<void> {
171
+ // Override in subclasses
172
+ }
173
+
174
+ /**
175
+ * Clean up all managed resources
176
+ */
177
+ public async cleanup(): Promise<void> {
178
+ // Return existing cleanup promise if already cleaning up
179
+ if (this.cleanupPromise) {
180
+ return this.cleanupPromise;
181
+ }
182
+
183
+ this.cleanupPromise = this.performCleanup();
184
+ return this.cleanupPromise;
185
+ }
186
+
187
+ private async performCleanup(): Promise<void> {
188
+ this.isShuttingDown = true;
189
+
190
+ // First, clean up child components
191
+ const childCleanupPromises: Promise<void>[] = [];
192
+ for (const child of this.childComponents) {
193
+ childCleanupPromises.push(child.cleanup());
194
+ }
195
+ await Promise.all(childCleanupPromises);
196
+ this.childComponents.clear();
197
+
198
+ // Clear all timers
199
+ for (const timer of this.timers) {
200
+ clearTimeout(timer);
201
+ }
202
+ this.timers.clear();
203
+
204
+ // Clear all intervals
205
+ for (const timer of this.intervals) {
206
+ clearInterval(timer);
207
+ }
208
+ this.intervals.clear();
209
+
210
+ // Remove all event listeners
211
+ for (const { target, event, handler } of this.listeners) {
212
+ // All listeners need to be removed, including 'once' listeners that might not have fired
213
+ if (typeof target.removeListener === 'function') {
214
+ target.removeListener(event, handler);
215
+ } else if (typeof target.removeEventListener === 'function') {
216
+ target.removeEventListener(event, handler);
217
+ }
218
+ }
219
+ this.listeners = [];
220
+
221
+ // Call subclass cleanup
222
+ await this.onCleanup();
223
+ }
224
+
225
+ /**
226
+ * Check if the component is shutting down
227
+ */
228
+ protected isShuttingDownState(): boolean {
229
+ return this.isShuttingDown;
230
+ }
231
+ }
package/ts/plugins.ts CHANGED
@@ -4,11 +4,12 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as https from 'https';
6
6
  import * as net from 'net';
7
+ import * as path from 'path';
7
8
  import * as tls from 'tls';
8
9
  import * as url from 'url';
9
10
  import * as http2 from 'http2';
10
11
 
11
- export { EventEmitter, fs, http, https, net, tls, url, http2 };
12
+ export { EventEmitter, fs, http, https, net, path, tls, url, http2 };
12
13
 
13
14
  // tsclass scope
14
15
  import * as tsclass from '@tsclass/tsclass';
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import { AsyncFileSystem } from '../../core/utils/fs-utils.js';
5
6
  import { type IHttpProxyOptions, type ICertificateEntry, type ILogger, createLogger } from './models/types.js';
6
7
  import type { IRouteConfig } from '../smart-proxy/models/route-types.js';
7
8
 
@@ -17,6 +18,7 @@ export class CertificateManager {
17
18
  private certificateStoreDir: string;
18
19
  private logger: ILogger;
19
20
  private httpsServer: plugins.https.Server | null = null;
21
+ private initialized = false;
20
22
 
21
23
  constructor(private options: IHttpProxyOptions) {
22
24
  this.certificateStoreDir = path.resolve(options.acme?.certificateStore || './certs');
@@ -24,6 +26,15 @@ export class CertificateManager {
24
26
 
25
27
  this.logger.warn('CertificateManager is deprecated - use SmartCertManager instead');
26
28
 
29
+ // Initialize synchronously for backward compatibility but log warning
30
+ this.initializeSync();
31
+ }
32
+
33
+ /**
34
+ * Synchronous initialization for backward compatibility
35
+ * @deprecated This uses sync filesystem operations which block the event loop
36
+ */
37
+ private initializeSync(): void {
27
38
  // Ensure certificate store directory exists
28
39
  try {
29
40
  if (!fs.existsSync(this.certificateStoreDir)) {
@@ -36,9 +47,28 @@ export class CertificateManager {
36
47
 
37
48
  this.loadDefaultCertificates();
38
49
  }
50
+
51
+ /**
52
+ * Async initialization - preferred method
53
+ */
54
+ public async initialize(): Promise<void> {
55
+ if (this.initialized) return;
56
+
57
+ // Ensure certificate store directory exists
58
+ try {
59
+ await AsyncFileSystem.ensureDir(this.certificateStoreDir);
60
+ this.logger.info(`Ensured certificate store directory: ${this.certificateStoreDir}`);
61
+ } catch (error) {
62
+ this.logger.warn(`Failed to create certificate store directory: ${error}`);
63
+ }
64
+
65
+ await this.loadDefaultCertificatesAsync();
66
+ this.initialized = true;
67
+ }
39
68
 
40
69
  /**
41
70
  * Loads default certificates from the filesystem
71
+ * @deprecated This uses sync filesystem operations which block the event loop
42
72
  */
43
73
  public loadDefaultCertificates(): void {
44
74
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -49,7 +79,28 @@ export class CertificateManager {
49
79
  key: fs.readFileSync(path.join(certPath, 'key.pem'), 'utf8'),
50
80
  cert: fs.readFileSync(path.join(certPath, 'cert.pem'), 'utf8')
51
81
  };
52
- this.logger.info('Loaded default certificates from filesystem');
82
+ this.logger.info('Loaded default certificates from filesystem (sync - deprecated)');
83
+ } catch (error) {
84
+ this.logger.error(`Failed to load default certificates: ${error}`);
85
+ this.generateSelfSignedCertificate();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Loads default certificates from the filesystem asynchronously
91
+ */
92
+ public async loadDefaultCertificatesAsync(): Promise<void> {
93
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
94
+ const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');
95
+
96
+ try {
97
+ const [key, cert] = await Promise.all([
98
+ AsyncFileSystem.readFile(path.join(certPath, 'key.pem')),
99
+ AsyncFileSystem.readFile(path.join(certPath, 'cert.pem'))
100
+ ]);
101
+
102
+ this.defaultCertificates = { key, cert };
103
+ this.logger.info('Loaded default certificates from filesystem (async)');
53
104
  } catch (error) {
54
105
  this.logger.error(`Failed to load default certificates: ${error}`);
55
106
  this.generateSelfSignedCertificate();