@j3r3mcdev/oast-server 1.1.5 → 1.1.7

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 (93) hide show
  1. package/.github/workflows/ci.yml +29 -29
  2. package/.github/workflows/publish.yml +31 -31
  3. package/README.md +192 -192
  4. package/jest.config.js +14 -14
  5. package/package.json +45 -45
  6. package/sadmin list shadows +9 -9
  7. package/src/api/controllers/__tests__/tasks.controller.test.ts +74 -74
  8. package/src/api/controllers/events.controller.ts +10 -10
  9. package/src/api/controllers/health.controller.ts +7 -7
  10. package/src/api/controllers/tasks.controller.ts +41 -41
  11. package/src/api/dto/__tests__/create-task.dto.test.ts +41 -41
  12. package/src/api/dto/__tests__/filter-tasks.dto.test.ts +35 -35
  13. package/src/api/dto/create-task.dto.ts +33 -33
  14. package/src/api/dto/filter-tasks.dto.ts +33 -33
  15. package/src/api/services/__tests__/events.service.test.ts +41 -41
  16. package/src/api/services/__tests__/tasks.service.test.ts +41 -41
  17. package/src/api/services/events.service.ts +17 -17
  18. package/src/api/services/tasks.service.ts +79 -79
  19. package/src/api/sse/events.stream.ts +90 -90
  20. package/src/bootstrap.ts +89 -89
  21. package/src/core/__tests__/core-router.test.ts +30 -30
  22. package/src/core/__tests__/core-server.test.ts +44 -44
  23. package/src/core/__tests__/event.normalizer.test.ts +56 -56
  24. package/src/core/__tests__/event.router.test.ts +89 -89
  25. package/src/core/__tests__/logger.test.ts +32 -32
  26. package/src/core/__tests__/storage-manager.test.ts +74 -74
  27. package/src/core/event.normalizer.ts +147 -147
  28. package/src/core/event.router.ts +13 -13
  29. package/src/core/http/__tests__/adapter-node.test.ts +52 -52
  30. package/src/core/http/__tests__/body-parser-multipart.test.ts +41 -41
  31. package/src/core/http/__tests__/body-parser-raw.test.ts +28 -28
  32. package/src/core/http/__tests__/body-parser-text.test.ts +28 -28
  33. package/src/core/http/__tests__/compile-path.test.ts +39 -39
  34. package/src/core/http/__tests__/middleware-pipeline.test.ts +51 -51
  35. package/src/core/http/__tests__/request.test.ts +34 -34
  36. package/src/core/http/__tests__/response.test.ts +35 -35
  37. package/src/core/http/__tests__/router-match.test.ts +171 -171
  38. package/src/core/http/adapter-node.ts +51 -51
  39. package/src/core/http/buildRequest.ts +18 -18
  40. package/src/core/http/compile-path.ts +32 -32
  41. package/src/core/http/errors.ts +37 -37
  42. package/src/core/http/http-server.ts +52 -52
  43. package/src/core/http/middleware.ts +160 -160
  44. package/src/core/http/request.ts +55 -55
  45. package/src/core/http/response.ts +93 -93
  46. package/src/core/http/router.ts +138 -138
  47. package/src/core/id-generator.ts +8 -8
  48. package/src/core/logger.ts +113 -113
  49. package/src/core/router.ts +44 -44
  50. package/src/core/server.ts +85 -85
  51. package/src/core/storage.ts +64 -64
  52. package/src/index.ts +14 -14
  53. package/src/listeners/api/__tests__/api.controller.test.ts +116 -116
  54. package/src/listeners/api/__tests__/api.extractor.test.ts +46 -46
  55. package/src/listeners/api/__tests__/api.listener.test.ts +82 -82
  56. package/src/listeners/api/__tests__/api.routes.test.ts +155 -155
  57. package/src/listeners/api/__tests__/api.sse.test.ts +105 -105
  58. package/src/listeners/api/api.controllers.ts +67 -67
  59. package/src/listeners/api/api.extractor.ts +43 -43
  60. package/src/listeners/api/api.listener.ts +50 -50
  61. package/src/listeners/api/api.routes.ts +76 -76
  62. package/src/listeners/api/api.sse.ts +38 -38
  63. package/src/listeners/dns/__tests__/dns.test.ts +118 -118
  64. package/src/listeners/dns/dns.extractor.ts +14 -14
  65. package/src/listeners/dns/dns.listener.ts +61 -61
  66. package/src/listeners/http/__tests__/http.extractor.test.ts +59 -59
  67. package/src/listeners/http/__tests__/http.listener.test.ts +133 -133
  68. package/src/listeners/http/http.extractor.ts +15 -15
  69. package/src/listeners/http/http.listener.ts +110 -110
  70. package/src/listeners/listener.interface.ts +4 -4
  71. package/src/listeners/smtp/__tests__/smtp.extractor.test.ts +69 -69
  72. package/src/listeners/smtp/__tests__/smtp.listener.test.ts +150 -150
  73. package/src/listeners/smtp/smtp.extractor.ts +18 -18
  74. package/src/listeners/smtp/smtp.listener.ts +60 -60
  75. package/src/listeners/ssrf/__tests__/ssrf.extractor.test.ts +41 -41
  76. package/src/listeners/ssrf/__tests__/ssrf.listener.test.ts +87 -87
  77. package/src/listeners/ssrf/ssrf.extractor.ts +14 -14
  78. package/src/listeners/ssrf/ssrf.listener.ts +37 -37
  79. package/src/listeners/tcp/tcp.extractor.ts +16 -16
  80. package/src/listeners/tcp/tcp.listener.ts +61 -61
  81. package/src/listeners/webhook/__tests__/webhook.extractor.test.ts +35 -35
  82. package/src/listeners/webhook/__tests__/webhook.listener.test.ts +122 -122
  83. package/src/listeners/webhook/webhook.extractor.ts +12 -12
  84. package/src/listeners/webhook/webhook.listener.ts +58 -58
  85. package/src/listeners/websocket/__tests__/websocket.extractor.test.ts +33 -33
  86. package/src/listeners/websocket/__tests__/websocket.listener.test.ts +90 -90
  87. package/src/listeners/websocket/websocket.extractor.ts +11 -11
  88. package/src/listeners/websocket/websocket.listener.ts +40 -40
  89. package/src/storage-adapters/adapters/__tests__/memory.storage.test.ts +75 -75
  90. package/src/storage-adapters/adapters/memory.storage.ts +64 -64
  91. package/src/storage-adapters/storage.interface.ts +26 -26
  92. package/src/types/event.types.ts +147 -147
  93. package/tsconfig.json +20 -21
@@ -1,41 +1,41 @@
1
- import { describe, it, expect, jest } from "@jest/globals";
2
- import { TasksService } from "../tasks.service";
3
- import { EventsService } from "../events.service";
4
- import { EventStreamManager } from "../../sse/events.stream";
5
-
6
- describe("TasksService", () => {
7
- it("émet un événement lors de la création d'une tâche", () => {
8
- const stream = new EventStreamManager({ heartbeatInterval: 999999 });
9
- const events = new EventsService(stream);
10
-
11
- const spy = jest.spyOn(stream, "broadcast");
12
-
13
- const service = new TasksService(events);
14
-
15
- const task = service.create({ type: "x", payload: {} });
16
-
17
- expect(spy).toHaveBeenCalledWith(
18
- "tasks",
19
- "task.created",
20
- expect.objectContaining({ id: task.id }),
21
- );
22
- });
23
-
24
- it("émet un événement lors de l'annulation d'une tâche", () => {
25
- const stream = new EventStreamManager({ heartbeatInterval: 999999 });
26
- const events = new EventsService(stream);
27
-
28
- const spy = jest.spyOn(stream, "broadcast");
29
-
30
- const service = new TasksService(events);
31
-
32
- const task = service.create({ type: "x", payload: {} });
33
- service.cancel(task.id);
34
-
35
- expect(spy).toHaveBeenCalledWith(
36
- "tasks",
37
- "task.cancelled",
38
- expect.objectContaining({ id: task.id }),
39
- );
40
- });
41
- });
1
+ import { describe, it, expect, jest } from "@jest/globals";
2
+ import { TasksService } from "../tasks.service";
3
+ import { EventsService } from "../events.service";
4
+ import { EventStreamManager } from "../../sse/events.stream";
5
+
6
+ describe("TasksService", () => {
7
+ it("émet un événement lors de la création d'une tâche", () => {
8
+ const stream = new EventStreamManager({ heartbeatInterval: 999999 });
9
+ const events = new EventsService(stream);
10
+
11
+ const spy = jest.spyOn(stream, "broadcast");
12
+
13
+ const service = new TasksService(events);
14
+
15
+ const task = service.create({ type: "x", payload: {} });
16
+
17
+ expect(spy).toHaveBeenCalledWith(
18
+ "tasks",
19
+ "task.created",
20
+ expect.objectContaining({ id: task.id }),
21
+ );
22
+ });
23
+
24
+ it("émet un événement lors de l'annulation d'une tâche", () => {
25
+ const stream = new EventStreamManager({ heartbeatInterval: 999999 });
26
+ const events = new EventsService(stream);
27
+
28
+ const spy = jest.spyOn(stream, "broadcast");
29
+
30
+ const service = new TasksService(events);
31
+
32
+ const task = service.create({ type: "x", payload: {} });
33
+ service.cancel(task.id);
34
+
35
+ expect(spy).toHaveBeenCalledWith(
36
+ "tasks",
37
+ "task.cancelled",
38
+ expect.objectContaining({ id: task.id }),
39
+ );
40
+ });
41
+ });
@@ -1,17 +1,17 @@
1
- import { EventStreamManager } from "../sse/events.stream";
2
-
3
- export class EventsService {
4
- constructor(private stream: EventStreamManager) {}
5
-
6
- connect(res: any, channels: string[] = []) {
7
- return this.stream.addClient(res, channels);
8
- }
9
-
10
- emit(channel: string, event: string, data: any) {
11
- this.stream.broadcast(channel, event, data);
12
- }
13
-
14
- emitAll(event: string, data: any) {
15
- this.stream.broadcastAll(event, data);
16
- }
17
- }
1
+ import { EventStreamManager } from "../sse/events.stream";
2
+
3
+ export class EventsService {
4
+ constructor(private stream: EventStreamManager) {}
5
+
6
+ connect(res: any, channels: string[] = []) {
7
+ return this.stream.addClient(res, channels);
8
+ }
9
+
10
+ emit(channel: string, event: string, data: any) {
11
+ this.stream.broadcast(channel, event, data);
12
+ }
13
+
14
+ emitAll(event: string, data: any) {
15
+ this.stream.broadcastAll(event, data);
16
+ }
17
+ }
@@ -1,79 +1,79 @@
1
- import { CreateTaskDto } from "../dto/create-task.dto";
2
- import { FilterTasksDto } from "../dto/filter-tasks.dto";
3
- import { EventsService } from "./events.service";
4
-
5
- export interface Task {
6
- id: string;
7
- type: string;
8
- payload: Record<string, any>;
9
- priority: "low" | "normal" | "high";
10
- metadata: Record<string, any>;
11
- status: "pending" | "running" | "completed" | "failed";
12
- createdAt: number;
13
- updatedAt: number;
14
- }
15
-
16
- export class TasksService {
17
- private tasks = new Map<string, Task>();
18
- private events: EventsService;
19
-
20
- constructor(events: EventsService) {
21
- this.events = events;
22
- }
23
-
24
- create(dto: CreateTaskDto): Task {
25
- const id = crypto.randomUUID();
26
- const now = Date.now();
27
-
28
- const task: Task = {
29
- id,
30
- type: dto.type,
31
- payload: dto.payload,
32
- priority: dto.priority ?? "normal",
33
- metadata: dto.metadata ?? {},
34
- status: "pending",
35
- createdAt: now,
36
- updatedAt: now,
37
- };
38
-
39
- this.tasks.set(id, task);
40
-
41
- this.events.emit("tasks", "task.created", task);
42
-
43
- return task;
44
- }
45
-
46
- get(id: string): Task | undefined {
47
- return this.tasks.get(id);
48
- }
49
-
50
- list(filters: FilterTasksDto): Task[] {
51
- let results = Array.from(this.tasks.values());
52
-
53
- if (filters.status) {
54
- results = results.filter((t) => t.status === filters.status);
55
- }
56
-
57
- if (filters.type) {
58
- results = results.filter((t) => t.type === filters.type);
59
- }
60
-
61
- if (filters.limit) {
62
- results = results.slice(0, filters.limit);
63
- }
64
-
65
- return results;
66
- }
67
-
68
- cancel(id: string): boolean {
69
- const task = this.tasks.get(id);
70
- if (!task) return false;
71
-
72
- task.status = "failed";
73
- task.updatedAt = Date.now();
74
-
75
- this.events.emit("tasks", "task.cancelled", task);
76
-
77
- return true;
78
- }
79
- }
1
+ import { CreateTaskDto } from "../dto/create-task.dto";
2
+ import { FilterTasksDto } from "../dto/filter-tasks.dto";
3
+ import { EventsService } from "./events.service";
4
+
5
+ export interface Task {
6
+ id: string;
7
+ type: string;
8
+ payload: Record<string, any>;
9
+ priority: "low" | "normal" | "high";
10
+ metadata: Record<string, any>;
11
+ status: "pending" | "running" | "completed" | "failed";
12
+ createdAt: number;
13
+ updatedAt: number;
14
+ }
15
+
16
+ export class TasksService {
17
+ private tasks = new Map<string, Task>();
18
+ private events: EventsService;
19
+
20
+ constructor(events: EventsService) {
21
+ this.events = events;
22
+ }
23
+
24
+ create(dto: CreateTaskDto): Task {
25
+ const id = crypto.randomUUID();
26
+ const now = Date.now();
27
+
28
+ const task: Task = {
29
+ id,
30
+ type: dto.type,
31
+ payload: dto.payload,
32
+ priority: dto.priority ?? "normal",
33
+ metadata: dto.metadata ?? {},
34
+ status: "pending",
35
+ createdAt: now,
36
+ updatedAt: now,
37
+ };
38
+
39
+ this.tasks.set(id, task);
40
+
41
+ this.events.emit("tasks", "task.created", task);
42
+
43
+ return task;
44
+ }
45
+
46
+ get(id: string): Task | undefined {
47
+ return this.tasks.get(id);
48
+ }
49
+
50
+ list(filters: FilterTasksDto): Task[] {
51
+ let results = Array.from(this.tasks.values());
52
+
53
+ if (filters.status) {
54
+ results = results.filter((t) => t.status === filters.status);
55
+ }
56
+
57
+ if (filters.type) {
58
+ results = results.filter((t) => t.type === filters.type);
59
+ }
60
+
61
+ if (filters.limit) {
62
+ results = results.slice(0, filters.limit);
63
+ }
64
+
65
+ return results;
66
+ }
67
+
68
+ cancel(id: string): boolean {
69
+ const task = this.tasks.get(id);
70
+ if (!task) return false;
71
+
72
+ task.status = "failed";
73
+ task.updatedAt = Date.now();
74
+
75
+ this.events.emit("tasks", "task.cancelled", task);
76
+
77
+ return true;
78
+ }
79
+ }
@@ -1,90 +1,90 @@
1
- // src/api/sse/event-stream.ts
2
-
3
- export interface EventStreamConfig {
4
- retry?: number;
5
- heartbeatInterval?: number;
6
- logger?: { info: Function; error: Function };
7
- }
8
-
9
- export interface StreamClient {
10
- id: string;
11
- res: any;
12
- channels: Set<string>;
13
- connectedAt: number;
14
- }
15
-
16
- export class EventStreamManager {
17
- private clients = new Map<string, StreamClient>();
18
- private config: Required<EventStreamConfig>;
19
- private heartbeatTimer: NodeJS.Timeout | null = null;
20
-
21
- constructor(config: EventStreamConfig = {}) {
22
- this.config = {
23
- retry: config.retry ?? 2000,
24
- heartbeatInterval: config.heartbeatInterval ?? 15000,
25
- logger: config.logger ?? console,
26
- };
27
-
28
- this.startHeartbeat();
29
- }
30
-
31
- private startHeartbeat() {
32
- if (process.env.NODE_ENV === "test") return;
33
-
34
- this.heartbeatTimer = setInterval(() => {
35
- for (const client of this.clients.values()) {
36
- client.res.write(`: heartbeat\n\n`);
37
- }
38
- }, this.config.heartbeatInterval);
39
- }
40
-
41
- addClient(res: any, channels: string[] = []): string {
42
- const id = crypto.randomUUID();
43
-
44
- const client: StreamClient = {
45
- id,
46
- res,
47
- channels: new Set(channels),
48
- connectedAt: Date.now(),
49
- };
50
-
51
- this.clients.set(id, client);
52
-
53
- res.write(`retry: ${this.config.retry}\n`);
54
- res.write(`event: connected\ndata: {"id":"${id}"}\n\n`);
55
-
56
- res.on("close", () => this.removeClient(id));
57
-
58
- this.config.logger.info(`SSE client connected: ${id}`);
59
-
60
- return id;
61
- }
62
-
63
- removeClient(id: string) {
64
- if (this.clients.has(id)) {
65
- this.clients.delete(id);
66
- this.config.logger.info(`SSE client disconnected: ${id}`);
67
- }
68
- }
69
-
70
- broadcast(channel: string, event: string, data: any) {
71
- const payload =
72
- `event: ${event}\n` +
73
- `data: ${JSON.stringify(data)}\n` +
74
- `channel: ${channel}\n\n`;
75
-
76
- for (const client of this.clients.values()) {
77
- if (client.channels.has(channel)) {
78
- client.res.write(payload);
79
- }
80
- }
81
- }
82
-
83
- broadcastAll(event: string, data: any) {
84
- const payload = `event: ${event}\n` + `data: ${JSON.stringify(data)}\n\n`;
85
-
86
- for (const client of this.clients.values()) {
87
- client.res.write(payload);
88
- }
89
- }
90
- }
1
+ // src/api/sse/event-stream.ts
2
+
3
+ export interface EventStreamConfig {
4
+ retry?: number;
5
+ heartbeatInterval?: number;
6
+ logger?: { info: Function; error: Function };
7
+ }
8
+
9
+ export interface StreamClient {
10
+ id: string;
11
+ res: any;
12
+ channels: Set<string>;
13
+ connectedAt: number;
14
+ }
15
+
16
+ export class EventStreamManager {
17
+ private clients = new Map<string, StreamClient>();
18
+ private config: Required<EventStreamConfig>;
19
+ private heartbeatTimer: NodeJS.Timeout | null = null;
20
+
21
+ constructor(config: EventStreamConfig = {}) {
22
+ this.config = {
23
+ retry: config.retry ?? 2000,
24
+ heartbeatInterval: config.heartbeatInterval ?? 15000,
25
+ logger: config.logger ?? console,
26
+ };
27
+
28
+ this.startHeartbeat();
29
+ }
30
+
31
+ private startHeartbeat() {
32
+ if (process.env.NODE_ENV === "test") return;
33
+
34
+ this.heartbeatTimer = setInterval(() => {
35
+ for (const client of this.clients.values()) {
36
+ client.res.write(`: heartbeat\n\n`);
37
+ }
38
+ }, this.config.heartbeatInterval);
39
+ }
40
+
41
+ addClient(res: any, channels: string[] = []): string {
42
+ const id = crypto.randomUUID();
43
+
44
+ const client: StreamClient = {
45
+ id,
46
+ res,
47
+ channels: new Set(channels),
48
+ connectedAt: Date.now(),
49
+ };
50
+
51
+ this.clients.set(id, client);
52
+
53
+ res.write(`retry: ${this.config.retry}\n`);
54
+ res.write(`event: connected\ndata: {"id":"${id}"}\n\n`);
55
+
56
+ res.on("close", () => this.removeClient(id));
57
+
58
+ this.config.logger.info(`SSE client connected: ${id}`);
59
+
60
+ return id;
61
+ }
62
+
63
+ removeClient(id: string) {
64
+ if (this.clients.has(id)) {
65
+ this.clients.delete(id);
66
+ this.config.logger.info(`SSE client disconnected: ${id}`);
67
+ }
68
+ }
69
+
70
+ broadcast(channel: string, event: string, data: any) {
71
+ const payload =
72
+ `event: ${event}\n` +
73
+ `data: ${JSON.stringify(data)}\n` +
74
+ `channel: ${channel}\n\n`;
75
+
76
+ for (const client of this.clients.values()) {
77
+ if (client.channels.has(channel)) {
78
+ client.res.write(payload);
79
+ }
80
+ }
81
+ }
82
+
83
+ broadcastAll(event: string, data: any) {
84
+ const payload = `event: ${event}\n` + `data: ${JSON.stringify(data)}\n\n`;
85
+
86
+ for (const client of this.clients.values()) {
87
+ client.res.write(payload);
88
+ }
89
+ }
90
+ }
package/src/bootstrap.ts CHANGED
@@ -1,89 +1,89 @@
1
- import { Logger } from "./core/logger";
2
- import { StorageManager } from "./core/storage";
3
- import { CoreRouter } from "./core/router";
4
- import { CoreServer } from "./core/server";
5
-
6
- // Listeners
7
- import { ApiListener } from "./listeners/api/api.listener";
8
- import { HttpListener } from "./listeners/http/http.listener";
9
- import { DnsListener } from "./listeners/dns/dns.listener";
10
- import { SmtpListener } from "./listeners/smtp/smtp.listener";
11
- import { TcpListener } from "./listeners/tcp/tcp.listener";
12
-
13
- async function main() {
14
- const logger = new Logger({ context: "Bootstrap" });
15
-
16
- // Core components
17
- const storage = new StorageManager({
18
- logger: logger.withContext("Storage"),
19
- });
20
-
21
- const router = new CoreRouter(storage, {
22
- logger: logger.withContext("CoreRouter"),
23
- });
24
-
25
- // ---------------------------
26
- // LISTENERS
27
- // ---------------------------
28
-
29
- // API → (router, storage, options)
30
- const apiListener = new ApiListener(storage, {
31
- port: 3100,
32
- logger: logger.withContext("ApiListener"),
33
- });
34
-
35
- // HTTP → (router, options)
36
- const httpListener = new HttpListener(router, {
37
- port: 8080,
38
- logger: logger.withContext("HttpListener"),
39
- });
40
-
41
- // DNS → (router, options)
42
- const dnsListener = new DnsListener(router, {
43
- server: {} as any,
44
- logger: logger.withContext("DnsListener"),
45
- });
46
-
47
- const smtpListener = new SmtpListener(router, {
48
- server: {} as any,
49
- logger: logger.withContext("SmtpListener"),
50
- });
51
-
52
- // TCP → (router, storage, options)
53
- const tcpListener = new TcpListener(router, storage, {
54
- port: 9000,
55
- logger: logger.withContext("TcpListener"),
56
- });
57
-
58
- // ---------------------------
59
- // CORE SERVER
60
- // ---------------------------
61
- const server = new CoreServer({
62
- logger: logger.withContext("CoreServer"),
63
- storage,
64
- listeners: [
65
- apiListener,
66
- httpListener,
67
- dnsListener,
68
- smtpListener,
69
- tcpListener,
70
- ],
71
- });
72
-
73
- await server.start();
74
-
75
- // Graceful shutdown
76
- const shutdown = async () => {
77
- logger.warn("Received shutdown signal, stopping server...");
78
- await server.stop();
79
- process.exit(0);
80
- };
81
-
82
- process.on("SIGINT", shutdown);
83
- process.on("SIGTERM", shutdown);
84
- }
85
-
86
- main().catch((err) => {
87
- console.error("Fatal error in bootstrap:", err);
88
- process.exit(1);
89
- });
1
+ import { Logger } from "./core/logger";
2
+ import { StorageManager } from "./core/storage";
3
+ import { CoreRouter } from "./core/router";
4
+ import { CoreServer } from "./core/server";
5
+
6
+ // Listeners
7
+ import { ApiListener } from "./listeners/api/api.listener";
8
+ import { HttpListener } from "./listeners/http/http.listener";
9
+ import { DnsListener } from "./listeners/dns/dns.listener";
10
+ import { SmtpListener } from "./listeners/smtp/smtp.listener";
11
+ import { TcpListener } from "./listeners/tcp/tcp.listener";
12
+
13
+ async function main() {
14
+ const logger = new Logger({ context: "Bootstrap" });
15
+
16
+ // Core components
17
+ const storage = new StorageManager({
18
+ logger: logger.withContext("Storage"),
19
+ });
20
+
21
+ const router = new CoreRouter(storage, {
22
+ logger: logger.withContext("CoreRouter"),
23
+ });
24
+
25
+ // ---------------------------
26
+ // LISTENERS
27
+ // ---------------------------
28
+
29
+ // API → (router, storage, options)
30
+ const apiListener = new ApiListener(storage, {
31
+ port: 3100,
32
+ logger: logger.withContext("ApiListener"),
33
+ });
34
+
35
+ // HTTP → (router, options)
36
+ const httpListener = new HttpListener(router, {
37
+ port: 8080,
38
+ logger: logger.withContext("HttpListener"),
39
+ });
40
+
41
+ // DNS → (router, options)
42
+ const dnsListener = new DnsListener(router, {
43
+ server: {} as any,
44
+ logger: logger.withContext("DnsListener"),
45
+ });
46
+
47
+ const smtpListener = new SmtpListener(router, {
48
+ server: {} as any,
49
+ logger: logger.withContext("SmtpListener"),
50
+ });
51
+
52
+ // TCP → (router, storage, options)
53
+ const tcpListener = new TcpListener(router, storage, {
54
+ port: 9000,
55
+ logger: logger.withContext("TcpListener"),
56
+ });
57
+
58
+ // ---------------------------
59
+ // CORE SERVER
60
+ // ---------------------------
61
+ const server = new CoreServer({
62
+ logger: logger.withContext("CoreServer"),
63
+ storage,
64
+ listeners: [
65
+ apiListener,
66
+ httpListener,
67
+ dnsListener,
68
+ smtpListener,
69
+ tcpListener,
70
+ ],
71
+ });
72
+
73
+ await server.start();
74
+
75
+ // Graceful shutdown
76
+ const shutdown = async () => {
77
+ logger.warn("Received shutdown signal, stopping server...");
78
+ await server.stop();
79
+ process.exit(0);
80
+ };
81
+
82
+ process.on("SIGINT", shutdown);
83
+ process.on("SIGTERM", shutdown);
84
+ }
85
+
86
+ main().catch((err) => {
87
+ console.error("Fatal error in bootstrap:", err);
88
+ process.exit(1);
89
+ });