@j3r3mcdev/oast-server 1.1.13 → 1.1.14

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 (105) hide show
  1. package/package.json +4 -1
  2. package/.env.example +0 -0
  3. package/.github/workflows/ci.yml +0 -29
  4. package/.github/workflows/publish.yml +0 -31
  5. package/image.png +0 -0
  6. package/jest.config.js +0 -14
  7. package/sadmin list shadows +0 -9
  8. package/src/api/controllers/__tests__/tasks.controller.test.ts +0 -74
  9. package/src/api/controllers/events.controller.ts +0 -10
  10. package/src/api/controllers/health.controller.ts +0 -7
  11. package/src/api/controllers/index.ts +0 -0
  12. package/src/api/controllers/tasks.controller.ts +0 -41
  13. package/src/api/dto/__tests__/create-task.dto.test.ts +0 -41
  14. package/src/api/dto/__tests__/filter-tasks.dto.test.ts +0 -35
  15. package/src/api/dto/create-task.dto.ts +0 -33
  16. package/src/api/dto/filter-tasks.dto.ts +0 -33
  17. package/src/api/services/__tests__/events.service.test.ts +0 -41
  18. package/src/api/services/__tests__/tasks.service.test.ts +0 -41
  19. package/src/api/services/events.service.ts +0 -17
  20. package/src/api/services/tasks.service.ts +0 -79
  21. package/src/api/sse/events.stream.ts +0 -90
  22. package/src/bootstrap.ts +0 -89
  23. package/src/config/constants.ts +0 -0
  24. package/src/config/env.ts +0 -0
  25. package/src/core/__tests__/core-router.test.ts +0 -30
  26. package/src/core/__tests__/core-server.test.ts +0 -44
  27. package/src/core/__tests__/event.normalizer.test.ts +0 -56
  28. package/src/core/__tests__/event.router.test.ts +0 -89
  29. package/src/core/__tests__/logger.test.ts +0 -32
  30. package/src/core/__tests__/storage-manager.test.ts +0 -74
  31. package/src/core/event.normalizer.ts +0 -167
  32. package/src/core/event.router.ts +0 -13
  33. package/src/core/http/__tests__/adapter-node.test.ts +0 -52
  34. package/src/core/http/__tests__/body-parser-multipart.test.ts +0 -41
  35. package/src/core/http/__tests__/body-parser-raw.test.ts +0 -28
  36. package/src/core/http/__tests__/body-parser-text.test.ts +0 -28
  37. package/src/core/http/__tests__/compile-path.test.ts +0 -39
  38. package/src/core/http/__tests__/middleware-pipeline.test.ts +0 -51
  39. package/src/core/http/__tests__/request.test.ts +0 -34
  40. package/src/core/http/__tests__/response.test.ts +0 -35
  41. package/src/core/http/__tests__/router-match.test.ts +0 -171
  42. package/src/core/http/adapter-node.ts +0 -51
  43. package/src/core/http/buildRequest.ts +0 -18
  44. package/src/core/http/compile-path.ts +0 -32
  45. package/src/core/http/errors.ts +0 -37
  46. package/src/core/http/http-server.ts +0 -52
  47. package/src/core/http/index.ts +0 -0
  48. package/src/core/http/main.ts +0 -0
  49. package/src/core/http/middleware.ts +0 -160
  50. package/src/core/http/request.ts +0 -55
  51. package/src/core/http/response.ts +0 -93
  52. package/src/core/http/router.ts +0 -138
  53. package/src/core/http/utils.ts +0 -0
  54. package/src/core/id-generator.ts +0 -8
  55. package/src/core/logger.ts +0 -113
  56. package/src/core/router.ts +0 -44
  57. package/src/core/server.ts +0 -85
  58. package/src/core/storage.ts +0 -64
  59. package/src/index.ts +0 -14
  60. package/src/listeners/api/__tests__/api.controller.test.ts +0 -116
  61. package/src/listeners/api/__tests__/api.extractor.test.ts +0 -46
  62. package/src/listeners/api/__tests__/api.listener.test.ts +0 -82
  63. package/src/listeners/api/__tests__/api.routes.test.ts +0 -155
  64. package/src/listeners/api/__tests__/api.sse.test.ts +0 -105
  65. package/src/listeners/api/api.controllers.ts +0 -67
  66. package/src/listeners/api/api.extractor.ts +0 -43
  67. package/src/listeners/api/api.listener.ts +0 -50
  68. package/src/listeners/api/api.routes.ts +0 -104
  69. package/src/listeners/api/api.sse.ts +0 -38
  70. package/src/listeners/dns/__tests__/dns.test.ts +0 -118
  71. package/src/listeners/dns/dns.extractor.ts +0 -14
  72. package/src/listeners/dns/dns.listener.ts +0 -61
  73. package/src/listeners/http/__tests__/http.extractor.test.ts +0 -59
  74. package/src/listeners/http/__tests__/http.listener.test.ts +0 -133
  75. package/src/listeners/http/http.extractor.ts +0 -15
  76. package/src/listeners/http/http.listener.ts +0 -110
  77. package/src/listeners/listener.interface.ts +0 -4
  78. package/src/listeners/smtp/__tests__/smtp.extractor.test.ts +0 -69
  79. package/src/listeners/smtp/__tests__/smtp.listener.test.ts +0 -150
  80. package/src/listeners/smtp/smtp.extractor.ts +0 -18
  81. package/src/listeners/smtp/smtp.listener.ts +0 -78
  82. package/src/listeners/ssrf/__tests__/ssrf.extractor.test.ts +0 -41
  83. package/src/listeners/ssrf/__tests__/ssrf.listener.test.ts +0 -87
  84. package/src/listeners/ssrf/ssrf.extractor.ts +0 -14
  85. package/src/listeners/ssrf/ssrf.listener.ts +0 -37
  86. package/src/listeners/tcp/tcp.extractor.ts +0 -16
  87. package/src/listeners/tcp/tcp.listener.ts +0 -61
  88. package/src/listeners/webhook/__tests__/webhook.extractor.test.ts +0 -35
  89. package/src/listeners/webhook/__tests__/webhook.listener.test.ts +0 -122
  90. package/src/listeners/webhook/webhook.extractor.ts +0 -12
  91. package/src/listeners/webhook/webhook.listener.ts +0 -58
  92. package/src/listeners/websocket/__tests__/websocket.extractor.test.ts +0 -33
  93. package/src/listeners/websocket/__tests__/websocket.listener.test.ts +0 -90
  94. package/src/listeners/websocket/websocket.extractor.ts +0 -11
  95. package/src/listeners/websocket/websocket.listener.ts +0 -40
  96. package/src/storage-adapters/adapters/__tests__/memory.storage.test.ts +0 -75
  97. package/src/storage-adapters/adapters/memory.storage.ts +0 -64
  98. package/src/storage-adapters/adapters/redis.storage.ts +0 -0
  99. package/src/storage-adapters/adapters/sqlite.storage.ts +0 -0
  100. package/src/storage-adapters/storage.interface.ts +0 -26
  101. package/src/types/event.types.ts +0 -166
  102. package/src/utils/token.ts +0 -0
  103. package/src-api.txt +0 -0
  104. package/src-architecture.txt +0 -0
  105. package/tsconfig.json +0 -20
@@ -1,50 +0,0 @@
1
- import { createServer, IncomingMessage, Server, ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
- import { StorageManager } from "../../core/storage";
4
- import { handleApiRequest } from "./api.routes";
5
- import { ApiSse } from "./api.sse";
6
-
7
- export interface ApiListenerOptions {
8
- port: number;
9
- logger?: Logger;
10
- }
11
-
12
- export class ApiListener {
13
- private logger: Logger;
14
- private server: Server | null = null;
15
- private sse: ApiSse;
16
-
17
- constructor(
18
- private storage: StorageManager,
19
- private options: ApiListenerOptions,
20
- ) {
21
- this.logger = options.logger ?? new Logger({ context: "ApiListener" });
22
- this.sse = new ApiSse(this.logger);
23
- }
24
-
25
- async start() {
26
- this.server = createServer((req: IncomingMessage, res: ServerResponse) => {
27
- void handleApiRequest(req, res, this.storage, this.sse, this.logger);
28
- });
29
-
30
- this.server.listen(this.options.port, () => {
31
- this.logger.info(`API Listener started on port ${this.options.port}`);
32
- });
33
- }
34
-
35
- async stop() {
36
- this.sse.closeAll();
37
-
38
- if (this.server) {
39
- this.server.close();
40
- this.server = null;
41
- }
42
-
43
- this.logger.info("API Listener stopped");
44
- }
45
-
46
- // Méthode appelée par ton CoreRouter / CoreServer après chaque event
47
- public broadcastEvent(event: any) {
48
- this.sse.broadcast(event);
49
- }
50
- }
@@ -1,104 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
- import { StorageManager } from "../../core/storage";
4
- import { ApiSse } from "./api.sse";
5
- import { ApiController } from "./api.controllers";
6
- import { EventNormalizer } from "../../core/event.normalizer";
7
-
8
- export async function handleApiRequest(
9
- req: IncomingMessage,
10
- res: ServerResponse,
11
- storage: StorageManager,
12
- sse: ApiSse,
13
- logger: Logger,
14
- ): Promise<void> {
15
- try {
16
- const url = new URL(req.url ?? "", `http://${req.headers.host}`);
17
- const path = url.pathname;
18
- const method = req.method ?? "GET";
19
-
20
- //
21
- // ---------------------------
22
- // POST / (API EVENT)
23
- // ---------------------------
24
- //
25
- if (method === "POST" && path === "/") {
26
- let body = "";
27
- req.on("data", (chunk) => (body += chunk));
28
- req.on("end", async () => {
29
- try {
30
- const parsed = JSON.parse(body);
31
-
32
- const normalized = EventNormalizer.normalizeApi({
33
- ip: req.socket.remoteAddress ?? "",
34
- body: parsed,
35
- raw: parsed,
36
- });
37
-
38
- await storage.save(normalized);
39
-
40
- res.writeHead(200, { "Content-Type": "application/json" });
41
- res.end(JSON.stringify({ success: true }));
42
- } catch (err: any) {
43
- logger.error("API POST error", err);
44
- res.writeHead(400, { "Content-Type": "application/json" });
45
- res.end(JSON.stringify({ success: false, error: err.message }));
46
- }
47
- });
48
- return;
49
- }
50
-
51
- //
52
- // ---------------------------
53
- // SSE STREAM
54
- // ---------------------------
55
- if (method === "GET" && path === "/events/stream") {
56
- await sse.handle(res);
57
- return;
58
- }
59
-
60
- //
61
- // ---------------------------
62
- // ROUTES REST
63
- // ---------------------------
64
- if (method === "GET" && path === "/events") {
65
- await ApiController.listEvents(url, res, storage);
66
- return;
67
- }
68
-
69
- if (method === "GET" && path.startsWith("/events/")) {
70
- const id = path.split("/")[2];
71
- await ApiController.getEvent(id, res, storage);
72
- return;
73
- }
74
-
75
- if (method === "DELETE" && path === "/events") {
76
- await ApiController.deleteAll(res, storage);
77
- return;
78
- }
79
-
80
- if (method === "DELETE" && path.startsWith("/events/")) {
81
- const id = path.split("/")[2];
82
- await ApiController.deleteOne(id, res, storage);
83
- return;
84
- }
85
-
86
- if (method === "GET" && path === "/stats") {
87
- await ApiController.stats(res, storage);
88
- return;
89
- }
90
-
91
- // 404
92
- res.writeHead(404, { "Content-Type": "application/json" });
93
- res.end(JSON.stringify({ success: false, error: "Not found" }));
94
- } catch (err: any) {
95
- logger.error("API error", { error: err?.message ?? String(err) });
96
- res.writeHead(500, { "Content-Type": "application/json" });
97
- res.end(
98
- JSON.stringify({
99
- success: false,
100
- error: err?.message ?? "Internal server error",
101
- }),
102
- );
103
- }
104
- }
@@ -1,38 +0,0 @@
1
- import { ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
-
4
- export class ApiSse {
5
- private clients: Set<ServerResponse> = new Set();
6
-
7
- constructor(private logger: Logger) {}
8
-
9
- handle(res: ServerResponse): void {
10
- res.writeHead(200, {
11
- "Content-Type": "text/event-stream",
12
- "Cache-Control": "no-cache",
13
- Connection: "keep-alive",
14
- });
15
-
16
- this.clients.add(res);
17
- res.write(`event: connected\ndata: "ok"\n\n`);
18
-
19
- res.on("close", () => {
20
- this.clients.delete(res);
21
- this.logger.debug?.("SSE client disconnected");
22
- });
23
- }
24
-
25
- broadcast(event: any): void {
26
- const payload = `event: event\ndata: ${JSON.stringify(event)}\n\n`;
27
- for (const client of this.clients) {
28
- client.write(payload);
29
- }
30
- }
31
-
32
- closeAll(): void {
33
- for (const client of this.clients) {
34
- client.end();
35
- }
36
- this.clients.clear();
37
- }
38
- }
@@ -1,118 +0,0 @@
1
- import { describe, it, expect, jest, beforeEach } from "@jest/globals";
2
- import { DnsListener } from "../dns.listener";
3
- import { EventNormalizer } from "../../../core/event.normalizer";
4
- import { CoreRouter } from "../../../core/router";
5
-
6
- // Mock EventNormalizer
7
- jest.mock("../../../core/event.normalizer", () => ({
8
- EventNormalizer: {
9
- normalizeDns: jest.fn(),
10
- },
11
- }));
12
-
13
- describe("DnsListener", () => {
14
- let router: CoreRouter;
15
- let server: any;
16
- let listener: DnsListener;
17
-
18
- beforeEach(() => {
19
- router = {
20
- dispatch: jest
21
- .fn<(event: any) => Promise<void>>()
22
- .mockResolvedValue(undefined),
23
- } as any;
24
-
25
- server = {
26
- on: jest.fn(),
27
- close: jest.fn(),
28
- };
29
-
30
- listener = new DnsListener(router, { server });
31
- });
32
-
33
- it("traite un paquet DNS valide et renvoie eventId", async () => {
34
- const send = jest.fn();
35
-
36
- (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
37
- id: "dns-123",
38
- source: "dns",
39
- });
40
-
41
- await listener.start();
42
-
43
- // simulate server.on("request", callback)
44
- const callback = server.on.mock.calls[0][1];
45
-
46
- const packet = {
47
- client: { address: "1.2.3.4" },
48
- questions: [{ name: "example.com", type: "A" }],
49
- };
50
-
51
- await callback(packet, send);
52
-
53
- expect(EventNormalizer.normalizeDns).toHaveBeenCalled();
54
- expect(router.dispatch).toHaveBeenCalled();
55
- expect(send).toHaveBeenCalledWith({
56
- success: true,
57
- eventId: "dns-123",
58
- });
59
- });
60
-
61
- it("renvoie une erreur si normalizeDns échoue", async () => {
62
- const send = jest.fn();
63
-
64
- (EventNormalizer.normalizeDns as jest.Mock).mockImplementation(() => {
65
- throw new Error("bad dns");
66
- });
67
-
68
- await listener.start();
69
-
70
- const callback = server.on.mock.calls[0][1];
71
-
72
- const packet = {
73
- client: { address: "1.2.3.4" },
74
- questions: [{ name: "example.com", type: "A" }],
75
- };
76
-
77
- await callback(packet, send);
78
-
79
- expect(send).toHaveBeenCalledWith({
80
- success: false,
81
- error: "bad dns",
82
- });
83
- });
84
-
85
- it("renvoie une erreur si router.dispatch échoue", async () => {
86
- const send = jest.fn();
87
-
88
- (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
89
- id: "dns-999",
90
- source: "dns",
91
- });
92
-
93
- (
94
- router.dispatch as jest.MockedFunction<(event: any) => Promise<void>>
95
- ).mockRejectedValueOnce(new Error("dispatch failed"));
96
-
97
- await listener.start();
98
-
99
- const callback = server.on.mock.calls[0][1];
100
-
101
- const packet = {
102
- client: { address: "1.2.3.4" },
103
- questions: [{ name: "example.com", type: "A" }],
104
- };
105
-
106
- await callback(packet, send);
107
-
108
- expect(send).toHaveBeenCalledWith({
109
- success: false,
110
- error: "dispatch failed",
111
- });
112
- });
113
-
114
- it("stop() appelle server.close si disponible", async () => {
115
- await listener.stop();
116
- expect(server.close).toHaveBeenCalled();
117
- });
118
- });
@@ -1,14 +0,0 @@
1
- export class DnsExtractor {
2
- static extract(packet: any) {
3
- if (!packet?.questions?.[0]) {
4
- throw new Error("Invalid DNS packet");
5
- }
6
-
7
- return {
8
- ip: packet.client?.address ?? "",
9
- query: packet.questions[0].name ?? "",
10
- type: packet.questions[0].type ?? "",
11
- raw: packet,
12
- };
13
- }
14
- }
@@ -1,61 +0,0 @@
1
- import { Logger } from "../../core/logger";
2
- import { CoreRouter } from "../../core/router";
3
- import { EventNormalizer } from "../../core/event.normalizer";
4
-
5
- export interface DnsListenerOptions {
6
- server: any; // instance dns2 ou autre
7
- logger?: Logger;
8
- }
9
-
10
- export class DnsListener {
11
- private logger: Logger;
12
-
13
- constructor(
14
- private router: CoreRouter,
15
- private options: DnsListenerOptions,
16
- ) {
17
- this.logger = options.logger ?? new Logger({ context: "DnsListener" });
18
- }
19
-
20
- async start(): Promise<void> {
21
- this.options.server.on("request", async (req: any, send: any) => {
22
- await this.handleQuery(req, send);
23
- });
24
-
25
- this.logger.info("DNS Listener started");
26
- }
27
-
28
- async stop(): Promise<void> {
29
- if (this.options.server.close) {
30
- this.options.server.close();
31
- }
32
- this.logger.info("DNS Listener stopped");
33
- }
34
-
35
- private async handleQuery(packet: any, send: any) {
36
- try {
37
- const rawEvent = {
38
- ip: packet?.client?.address ?? "",
39
- query: packet?.questions?.[0]?.name ?? "",
40
- recordType: packet?.questions?.[0]?.type ?? "",
41
- raw: packet,
42
- };
43
-
44
- const normalized = EventNormalizer.normalizeDns(rawEvent);
45
-
46
- await this.router.dispatch(normalized);
47
-
48
- send({
49
- success: true,
50
- eventId: normalized.id,
51
- });
52
- } catch (err: any) {
53
- this.logger.error("DNS Listener error", { error: err.message });
54
-
55
- send({
56
- success: false,
57
- error: err.message,
58
- });
59
- }
60
- }
61
- }
@@ -1,59 +0,0 @@
1
- import { describe, it, expect } from "@jest/globals";
2
- import { HttpExtractor } from "../http.extractor";
3
-
4
- describe("HttpExtractor", () => {
5
- it("extrait correctement un RawEvent complet", () => {
6
- const req: any = {
7
- ip: "1.1.1.1",
8
- method: "POST",
9
- path: "/login",
10
- url: "/login",
11
- headers: {
12
- "x-forwarded-for": "2.2.2.2",
13
- host: "localhost",
14
- },
15
- query: { a: 1 },
16
- body: { user: "admin" },
17
- };
18
-
19
- const extracted = HttpExtractor.extract(req);
20
-
21
- expect(extracted).toEqual({
22
- ip: "1.1.1.1", // priorité à req.ip
23
- method: "POST",
24
- path: "/login",
25
- headers: req.headers,
26
- query: { a: 1 },
27
- body: { user: "admin" },
28
- raw: req,
29
- });
30
- });
31
-
32
- it("utilise x-forwarded-for si req.ip est absent", () => {
33
- const req: any = {
34
- method: "GET",
35
- url: "/",
36
- headers: { "x-forwarded-for": "9.9.9.9" },
37
- };
38
-
39
- const extracted = HttpExtractor.extract(req);
40
-
41
- expect(extracted.ip).toBe("9.9.9.9");
42
- });
43
-
44
- it("retourne une structure complète même si des champs manquent", () => {
45
- const req: any = {};
46
-
47
- const extracted = HttpExtractor.extract(req);
48
-
49
- expect(extracted).toEqual({
50
- ip: "",
51
- method: undefined,
52
- path: undefined,
53
- headers: {},
54
- query: {},
55
- body: null,
56
- raw: req,
57
- });
58
- });
59
- });
@@ -1,133 +0,0 @@
1
- import {
2
- describe,
3
- it,
4
- expect,
5
- jest,
6
- beforeEach,
7
- afterEach,
8
- } from "@jest/globals";
9
- import http from "http";
10
- import { HttpListener } from "../http.listener";
11
- import { EventNormalizer } from "../../../core/event.normalizer";
12
-
13
- // Mock EventNormalizer
14
- jest.mock("../../../core/event.normalizer", () => ({
15
- EventNormalizer: {
16
- normalizeHttp: jest.fn(),
17
- },
18
- }));
19
-
20
- // Helper pour requêtes HTTP
21
- async function makeRequest(port: number, path: string): Promise<string> {
22
- return new Promise((resolve) => {
23
- const req = http.request(
24
- {
25
- hostname: "localhost",
26
- port,
27
- path,
28
- method: "GET",
29
- },
30
- (res) => {
31
- let data = "";
32
- res.on("data", (chunk) => (data += chunk));
33
- res.on("end", () => resolve(data));
34
- },
35
- );
36
-
37
- req.end();
38
- });
39
- }
40
-
41
- describe("HttpListener (PRO)", () => {
42
- let listener: HttpListener;
43
- let router: { dispatch: jest.Mock };
44
- let port: number;
45
-
46
- beforeEach(() => {
47
- router = {
48
- dispatch: jest.fn(),
49
- };
50
-
51
- port = 10000 + Math.floor(Math.random() * 5000);
52
- });
53
-
54
- afterEach(async () => {
55
- if (listener) {
56
- await listener.stop();
57
- }
58
- });
59
-
60
- it("normalise, dispatch et renvoie un eventId", async () => {
61
- listener = new HttpListener(router as any, { port });
62
- await listener.start();
63
-
64
- // NormalizedHttpEvent VALIDE
65
- (EventNormalizer.normalizeHttp as jest.Mock).mockReturnValue({
66
- id: "evt-123",
67
- type: "http",
68
- timestamp: Date.now(),
69
- sourceIp: "127.0.0.1",
70
- request: {
71
- method: "GET",
72
- path: "/test",
73
- headers: {},
74
- query: {},
75
- body: "",
76
- },
77
- });
78
-
79
- const response = await makeRequest(port, "/test");
80
- const json = JSON.parse(response);
81
-
82
- expect(EventNormalizer.normalizeHttp).toHaveBeenCalled();
83
- expect(router.dispatch).toHaveBeenCalled();
84
- expect(json).toEqual({
85
- success: true,
86
- eventId: "evt-123",
87
- });
88
- });
89
-
90
- it("renvoie 500 si normalizeHttp lance une erreur", async () => {
91
- listener = new HttpListener(router as any, { port });
92
- await listener.start();
93
-
94
- (EventNormalizer.normalizeHttp as jest.Mock).mockImplementation(() => {
95
- throw new Error("bad request");
96
- });
97
-
98
- const response = await makeRequest(port, "/err");
99
- const json = JSON.parse(response);
100
-
101
- expect(json.success).toBe(false);
102
- expect(json.error).toBe("bad request");
103
- });
104
-
105
- it("renvoie 500 si router.dispatch rejette une erreur", async () => {
106
- listener = new HttpListener(router as any, { port });
107
- await listener.start();
108
-
109
- (EventNormalizer.normalizeHttp as jest.Mock).mockReturnValue({
110
- id: "evt-999",
111
- type: "http",
112
- timestamp: Date.now(),
113
- sourceIp: "127.0.0.1",
114
- request: {
115
- method: "GET",
116
- path: "/dispatch-error",
117
- headers: {},
118
- query: {},
119
- body: "",
120
- },
121
- });
122
-
123
- (router.dispatch as any).mockRejectedValueOnce(
124
- new Error("dispatch failed"),
125
- );
126
-
127
- const response = await makeRequest(port, "/dispatch-error");
128
- const json = JSON.parse(response);
129
-
130
- expect(json.success).toBe(false);
131
- expect(json.error).toBe("dispatch failed");
132
- });
133
- });
@@ -1,15 +0,0 @@
1
- export class HttpExtractor {
2
- static extract(req: any) {
3
- const headers = req.headers ?? {};
4
-
5
- return {
6
- ip: req.ip ?? headers["x-forwarded-for"] ?? "",
7
- method: req.method, // ← PAS de fallback ""
8
- path: req.path ?? req.url, // ← PAS de fallback ""
9
- headers,
10
- query: req.query ?? {},
11
- body: req.body ?? null,
12
- raw: req,
13
- };
14
- }
15
- }
@@ -1,110 +0,0 @@
1
- import http, { IncomingMessage, ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
- import { CoreRouter } from "../../core/router";
4
- import { EventNormalizer } from "../../core/event.normalizer";
5
-
6
- export interface HttpListenerOptions {
7
- port: number;
8
- logger?: Logger;
9
- }
10
-
11
- export class HttpListener {
12
- private server?: http.Server;
13
- private logger: Logger;
14
-
15
- constructor(
16
- private router: CoreRouter,
17
- private options: HttpListenerOptions,
18
- ) {
19
- this.logger = options.logger ?? new Logger({ context: "HttpListener" });
20
- }
21
-
22
- async start(): Promise<void> {
23
- return new Promise((resolve) => {
24
- this.server = http.createServer(async (req, res) => {
25
- await this.handleRequest(req, res);
26
- });
27
-
28
- this.server.listen(this.options.port, () => {
29
- this.logger.info(`HTTP Listener started on port ${this.options.port}`);
30
- resolve();
31
- });
32
- });
33
- }
34
-
35
- async stop(): Promise<void> {
36
- return new Promise((resolve, reject) => {
37
- if (!this.server) return resolve();
38
-
39
- this.server.close((err) => {
40
- if (err) return reject(err);
41
- this.logger.info("HTTP Listener stopped");
42
- resolve();
43
- });
44
- });
45
- }
46
-
47
- private async handleRequest(req: IncomingMessage, res: ServerResponse) {
48
- try {
49
- const body = await this.readBody(req);
50
-
51
- const ipHeader = req.headers["x-forwarded-for"];
52
- const ip =
53
- typeof ipHeader === "string"
54
- ? ipHeader
55
- : Array.isArray(ipHeader)
56
- ? ipHeader[0]
57
- : (req.socket.remoteAddress ?? "");
58
-
59
- const rawEvent = {
60
- ip,
61
- method: req.method ?? "",
62
- path: req.url ?? "",
63
- headers: req.headers,
64
- query: {}, // parsing optionnel
65
- body,
66
- raw: req,
67
- };
68
-
69
- const normalized = EventNormalizer.normalizeHttp(rawEvent);
70
-
71
- await this.router.dispatch(normalized);
72
-
73
- res.writeHead(200, { "Content-Type": "application/json" });
74
- res.end(
75
- JSON.stringify({
76
- success: true,
77
- eventId: normalized.id,
78
- }),
79
- );
80
- } catch (err: any) {
81
- this.logger.error("HTTP Listener error", err);
82
-
83
- res.writeHead(500, { "Content-Type": "application/json" });
84
- res.end(
85
- JSON.stringify({
86
- success: false,
87
- error: err.message,
88
- }),
89
- );
90
- }
91
- }
92
-
93
- private readBody(req: IncomingMessage): Promise<any> {
94
- return new Promise((resolve) => {
95
- let data = "";
96
-
97
- req.on("data", (chunk) => {
98
- data += chunk;
99
- });
100
-
101
- req.on("end", () => {
102
- try {
103
- resolve(JSON.parse(data || "{}"));
104
- } catch {
105
- resolve(data);
106
- }
107
- });
108
- });
109
- }
110
- }