104 lines
2.8 KiB
JavaScript
104 lines
2.8 KiB
JavaScript
import http from "node:http";
|
|
|
|
const port = Number.parseInt(process.env.PORT ?? "8787", 10);
|
|
const webhookPath = process.env.OPENCLAW_SMOKE_PATH?.trim() || "/webhook";
|
|
const expectedAuthHeader = process.env.OPENCLAW_SMOKE_AUTH?.trim() || "";
|
|
const maxBodyBytes = 1_000_000;
|
|
const maxEvents = 200;
|
|
|
|
const events = [];
|
|
let nextId = 1;
|
|
|
|
function writeJson(res, status, payload) {
|
|
res.statusCode = status;
|
|
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
res.end(JSON.stringify(payload));
|
|
}
|
|
|
|
function readBody(req) {
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = [];
|
|
let total = 0;
|
|
req.on("data", (chunk) => {
|
|
total += chunk.length;
|
|
if (total > maxBodyBytes) {
|
|
reject(new Error("payload_too_large"));
|
|
req.destroy();
|
|
return;
|
|
}
|
|
chunks.push(chunk);
|
|
});
|
|
req.on("end", () => {
|
|
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
});
|
|
req.on("error", reject);
|
|
});
|
|
}
|
|
|
|
function trimEvents() {
|
|
if (events.length <= maxEvents) return;
|
|
events.splice(0, events.length - maxEvents);
|
|
}
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const method = req.method ?? "GET";
|
|
const url = req.url ?? "/";
|
|
|
|
if (method === "GET" && url === "/health") {
|
|
writeJson(res, 200, { ok: true, webhookPath, events: events.length });
|
|
return;
|
|
}
|
|
|
|
if (method === "GET" && url === "/events") {
|
|
writeJson(res, 200, { count: events.length, events });
|
|
return;
|
|
}
|
|
|
|
if (method === "POST" && url === "/reset") {
|
|
events.length = 0;
|
|
writeJson(res, 200, { ok: true });
|
|
return;
|
|
}
|
|
|
|
if (method === "POST" && url === webhookPath) {
|
|
const authorization = req.headers.authorization ?? "";
|
|
if (expectedAuthHeader && authorization !== expectedAuthHeader) {
|
|
writeJson(res, 401, { error: "unauthorized" });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const raw = await readBody(req);
|
|
let body = null;
|
|
try {
|
|
body = raw.length > 0 ? JSON.parse(raw) : null;
|
|
} catch {
|
|
body = { raw };
|
|
}
|
|
|
|
const event = {
|
|
id: `evt-${nextId++}`,
|
|
receivedAt: new Date().toISOString(),
|
|
method,
|
|
path: url,
|
|
authorizationPresent: Boolean(authorization),
|
|
body,
|
|
};
|
|
events.push(event);
|
|
trimEvents();
|
|
writeJson(res, 200, { ok: true, received: true, eventId: event.id, count: events.length });
|
|
} catch (err) {
|
|
const code = err instanceof Error && err.message === "payload_too_large" ? 413 : 500;
|
|
writeJson(res, code, { error: err instanceof Error ? err.message : "unknown_error" });
|
|
}
|
|
return;
|
|
}
|
|
|
|
writeJson(res, 404, { error: "not_found" });
|
|
});
|
|
|
|
server.listen(port, "0.0.0.0", () => {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`[openclaw-smoke] listening on :${port} path=${webhookPath}`);
|
|
});
|