-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.ts
More file actions
104 lines (84 loc) · 3.59 KB
/
server.ts
File metadata and controls
104 lines (84 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { Context, Hono } from "hono";
import { compress } from "hono/compress";
import { serve, ServerType } from "@hono/node-server";
import { SignJWT, jwtVerify } from "jose";
import { IngestionClient } from "./ingestionClient";
import logger from "./logger";
import { z } from "zod";
export const loginSchema = z.object({
username: z.string(),
password: z.string(),
});
export const ingestionSchema = z.array(z.record(z.unknown()));
export type loginType = z.infer<typeof loginSchema>;
export type ingestionType = z.infer<typeof ingestionSchema>;
export class SharedServer {
public app: Hono;
public server?: ServerType;
private JWT_SECRET: Uint8Array;
constructor() {
this.app = new Hono();
this.app.use(compress());
this.JWT_SECRET = new TextEncoder().encode("sentinel-any-event-collector-secret");
}
start(ingestionClient: IngestionClient) {
this.server = serve(this.app);
logger.info(`Server started on http://localhost:3000`);
this.app.all(async (c, next) => {
logger.info(`Received ${c.req.method} request on ${c.req.url}`);
await next();
});
// Auth
const authMiddleware = async (c: Context, next: () => Promise<void>) => {
const authHeader = c.req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return c.json({ error: "Unauthorized" }, 401);
}
const token = authHeader.split(" ")[1];
try {
const { payload } = await jwtVerify(token, this.JWT_SECRET);
c.set("user", payload);
await next();
} catch (error) {
return c.json({ error: "Invalid or expired token" }, 403);
}
};
// Login endpoint
this.app.post("/login", async (c) => {
const body = await c.req.json();
const result = loginSchema.safeParse(body);
if (!result.success) {
return c.json({ error: result.error.format() }, 400);
}
const { username, password } = result.data;
if (username === "admin" && password === "password") {
const token = await new SignJWT({ username })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("1h")
.sign(this.JWT_SECRET);
return c.json({ message: "Login Succesful", token });
}
return c.json({ error: "Invalid credentials" }, 401);
});
// Ingestion endpoint
this.app.post("/data", authMiddleware, async (c) => {
if (c.req.header("Content-Type") !== "application/json") {
return c.json({ error: "Invalid content type" }, 400);
}
try {
const queryParams = c.req.query();
const body = await c.req.json(); // Parse JSON request body
const result = ingestionSchema.safeParse(body);
if (!result.success) {
return c.json({ error: result.error.format() }, 400);
}
const sourceType = queryParams["sourceType"] || "Custom";
const sourceIp = c.req.header("X-Forwarded-For") || "";
ingestionClient.ingest(result.data, sourceType, sourceIp);
return c.json({ message: "Data received, Ingesting Logs" });
} catch (error) {
return c.json({ error: "Invalid JSON" }, 400);
}
});
}
}