perf: optimize monitor runner, fix SSE leak, deduplicate shared utils

This commit is contained in:
2026-03-18 18:44:08 +04:00
parent 980261632e
commit 425bfbfc39
16 changed files with 141 additions and 108 deletions
+5 -1
View File
@@ -1,6 +1,10 @@
import postgres from "postgres";
const sql = postgres(process.env.DATABASE_URL ?? "postgres://pingql:pingql@localhost:5432/pingql");
const sql = postgres(process.env.DATABASE_URL ?? "postgres://pingql:pingql@localhost:5432/pingql", {
max: 20,
idle_timeout: 30,
connect_timeout: 10,
});
export default sql;
-24
View File
@@ -5,7 +5,6 @@ import { monitors } from "./routes/monitors";
import { account } from "./routes/auth";
import { internal } from "./routes/internal";
import { migrate } from "./db";
await migrate();
const CORS_ORIGIN = process.env.CORS_ORIGINS?.split(",") ?? ["https://pingql.com"];
@@ -16,29 +15,6 @@ const CORS_HEADERS = {
"access-control-allow-headers": "Content-Type, Authorization",
};
// ── Rate limiter ──────────────────────────────────────────────────────
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
const RATE_LIMIT_WINDOW = 60_000; // 1 minute
function rateLimit(ip: string, maxRequests: number): boolean {
const now = Date.now();
const entry = rateLimitMap.get(ip);
if (!entry || now > entry.resetAt) {
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW });
return true;
}
entry.count++;
return entry.count <= maxRequests;
}
// Cleanup stale entries every 5 minutes
setInterval(() => {
const now = Date.now();
for (const [key, entry] of rateLimitMap) {
if (now > entry.resetAt) rateLimitMap.delete(key);
}
}, 5 * 60_000);
const SECURITY_HEADERS = {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
+4
View File
@@ -317,6 +317,10 @@ export function validateQuery(query: unknown, path = ""): ValidationError[] {
if (typeof value !== "string") {
errors.push({ path: keyPath, message: `${key} expects a string` });
}
} else if (key === "$consider") {
if (value !== "up" && value !== "down") {
errors.push({ path: keyPath, message: '$consider must be "up" or "down"' });
}
} else if (key.startsWith("$")) {
// It's an operator inside a field condition — skip validation here
} else {
+2 -19
View File
@@ -1,27 +1,10 @@
import { Elysia, t } from "elysia";
import { createHmac, randomBytes } from "crypto";
import sql from "../db";
import { createRateLimiter } from "../utils/rate-limit";
// ── Per-IP rate limiting for auth endpoints ───────────────────────────
const authRateMap = new Map<string, { count: number; resetAt: number }>();
function checkAuthRateLimit(ip: string, maxPerMinute: number): boolean {
const now = Date.now();
const entry = authRateMap.get(ip);
if (!entry || now > entry.resetAt) {
authRateMap.set(ip, { count: 1, resetAt: now + 60_000 });
return true;
}
entry.count++;
return entry.count <= maxPerMinute;
}
setInterval(() => {
const now = Date.now();
for (const [key, entry] of authRateMap) {
if (now > entry.resetAt) authRateMap.delete(key);
}
}, 5 * 60_000);
const checkAuthRateLimit = createRateLimiter();
const EMAIL_HMAC_KEY = process.env.EMAIL_HMAC_KEY || "pingql-default-hmac-key";
+1 -9
View File
@@ -2,16 +2,8 @@
/// Protected by MONITOR_TOKEN — not exposed to users.
import { Elysia } from "elysia";
import { timingSafeEqual } from "crypto";
import sql from "../db";
function safeTokenCompare(a: string | undefined, b: string | undefined): boolean {
if (!a || !b) return false;
const bufA = Buffer.from(a);
const bufB = Buffer.from(b);
if (bufA.length !== bufB.length) return false;
return timingSafeEqual(bufA, bufB);
}
import { safeTokenCompare } from "../utils/token";
export async function pruneOldPings(retentionDays = 90) {
const result = await sql`DELETE FROM pings WHERE checked_at < now() - ${retentionDays + ' days'}::interval`;
+1 -1
View File
@@ -113,7 +113,7 @@ export const monitors = new Elysia({ prefix: "/monitors" })
SELECT id FROM monitors WHERE id = ${params.id} AND account_id = ${accountId}
`;
if (!monitor) return error(404, { error: "Not found" });
const limit = Math.min(Number(query.limit ?? 100), 1000);
const limit = Math.min(Number(query.limit) || 100, 1000);
return sql`
SELECT * FROM pings
WHERE monitor_id = ${params.id}
+6 -10
View File
@@ -1,15 +1,7 @@
import { Elysia, t } from "elysia";
import { timingSafeEqual } from "crypto";
import sql from "../db";
import { resolveKey } from "./auth";
function safeTokenCompare(a: string | undefined, b: string | undefined): boolean {
if (!a || !b) return false;
const bufA = Buffer.from(a);
const bufB = Buffer.from(b);
if (bufA.length !== bufB.length) return false;
return timingSafeEqual(bufA, bufB);
}
import { safeTokenCompare } from "../utils/token";
// ── SSE bus ───────────────────────────────────────────────────────────────────
type SSEController = ReadableStreamDefaultController<Uint8Array>;
@@ -35,7 +27,11 @@ function makeSSEStream(accountId: string): Response {
bus.get(accountId)!.add(ctrl);
ctrl.enqueue(enc.encode(": connected\n\n"));
heartbeat = setInterval(() => {
try { ctrl.enqueue(enc.encode(": heartbeat\n\n")); } catch { clearInterval(heartbeat); }
try { ctrl.enqueue(enc.encode(": heartbeat\n\n")); } catch {
clearInterval(heartbeat);
bus.get(accountId)?.delete(ctrl);
if (bus.get(accountId)?.size === 0) bus.delete(accountId);
}
}, 10_000);
},
cancel() {
+21
View File
@@ -0,0 +1,21 @@
export function createRateLimiter(windowMs = 60_000, cleanupIntervalMs = 5 * 60_000) {
const map = new Map<string, { count: number; resetAt: number }>();
setInterval(() => {
const now = Date.now();
for (const [key, entry] of map) {
if (now > entry.resetAt) map.delete(key);
}
}, cleanupIntervalMs);
return function check(key: string, max: number): boolean {
const now = Date.now();
const entry = map.get(key);
if (!entry || now > entry.resetAt) {
map.set(key, { count: 1, resetAt: now + windowMs });
return true;
}
entry.count++;
return entry.count <= max;
};
}
+9
View File
@@ -0,0 +1,9 @@
import { timingSafeEqual } from "crypto";
export function safeTokenCompare(a: string | undefined, b: string | undefined): boolean {
if (!a || !b) return false;
const bufA = Buffer.from(a);
const bufB = Buffer.from(b);
if (bufA.length !== bufB.length) return false;
return timingSafeEqual(bufA, bufB);
}