refactor: improve maintainability by reducing LOC/reuse

This commit is contained in:
2026-03-28 16:52:19 +04:00
parent 8831c9c7b4
commit 6dcb5c0a52
29 changed files with 311 additions and 468 deletions
+2 -73
View File
@@ -1,4 +1,5 @@
import postgres from "postgres";
import { migrate as sharedMigrate } from "../../shared/db";
const sql = postgres(process.env.DATABASE_URL ?? "postgres://pingql:pingql@localhost:5432/pingql", {
max: 20,
@@ -9,77 +10,5 @@ const sql = postgres(process.env.DATABASE_URL ?? "postgres://pingql:pingql@local
export default sql;
export async function migrate() {
await sql`CREATE EXTENSION IF NOT EXISTS pgcrypto`;
await sql`
CREATE TABLE IF NOT EXISTS accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key TEXT NOT NULL UNIQUE,
email_hash TEXT,
created_at TIMESTAMPTZ DEFAULT now()
)
`;
await sql`
CREATE TABLE IF NOT EXISTS monitors (
id TEXT PRIMARY KEY DEFAULT encode(gen_random_bytes(8), 'hex'),
account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
name TEXT NOT NULL,
url TEXT NOT NULL,
method TEXT NOT NULL DEFAULT 'GET',
request_headers JSONB,
request_body TEXT,
timeout_ms INTEGER NOT NULL DEFAULT 30000,
interval_s INTEGER NOT NULL DEFAULT 60,
query JSONB,
enabled BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
)
`;
await sql`
CREATE TABLE IF NOT EXISTS pings (
id BIGSERIAL PRIMARY KEY,
monitor_id TEXT NOT NULL REFERENCES monitors(id) ON DELETE CASCADE,
checked_at TIMESTAMPTZ NOT NULL DEFAULT now(),
scheduled_at TIMESTAMPTZ,
jitter_ms INTEGER,
status_code INTEGER,
latency_ms INTEGER,
up BOOLEAN NOT NULL,
error TEXT,
meta JSONB
)
`;
// Migrations for existing deployments
await sql`ALTER TABLE pings ADD COLUMN IF NOT EXISTS scheduled_at TIMESTAMPTZ`;
await sql`ALTER TABLE pings ADD COLUMN IF NOT EXISTS jitter_ms INTEGER`;
await sql`ALTER TABLE accounts ADD COLUMN IF NOT EXISTS plan TEXT NOT NULL DEFAULT 'free'`;
await sql`ALTER TABLE accounts ADD COLUMN IF NOT EXISTS plan_expires_at TIMESTAMPTZ`;
await sql`ALTER TABLE accounts ADD COLUMN IF NOT EXISTS plan_stack JSONB NOT NULL DEFAULT '[]'`;
await sql`CREATE INDEX IF NOT EXISTS idx_pings_monitor ON pings(monitor_id, checked_at DESC)`;
await sql`CREATE INDEX IF NOT EXISTS idx_pings_checked_at ON pings(checked_at)`;
// Response bodies stored separately to keep pings table lean
await sql`
CREATE TABLE IF NOT EXISTS ping_bodies (
ping_id BIGINT PRIMARY KEY REFERENCES pings(id) ON DELETE CASCADE,
body TEXT
)
`;
await sql`
CREATE TABLE IF NOT EXISTS api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key TEXT NOT NULL UNIQUE,
account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
label TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
last_used_at TIMESTAMPTZ
)
`;
console.log("DB ready");
await sharedMigrate(sql);
}
+1 -7
View File
@@ -6,13 +6,7 @@ import { migrate } from "./db";
await migrate();
const SECURITY_HEADERS = {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Strict-Transport-Security": "max-age=63072000; includeSubDomains",
"X-XSS-Protection": "0",
"Referrer-Policy": "strict-origin-when-cross-origin",
};
import { SECURITY_HEADERS } from "../../shared/auth";
const app = new Elysia()
.onAfterHandle(({ set }) => {
+5 -38
View File
@@ -1,9 +1,7 @@
import { Elysia, t } from "elysia";
import { createHmac, randomBytes } from "crypto";
import sql from "../db";
import { createRateLimiter } from "../utils/rate-limit";
const EMAIL_HMAC_KEY = process.env.EMAIL_HMAC_KEY || "pingql-default-hmac-key";
import { createRateLimiter } from "../../../shared/rate-limit";
import { generateKey, hashEmail, resolveKey as sharedResolveKey, extractAuthKey, COOKIE_OPTS } from "../../../shared/auth";
function redir(to: string) {
return new Response(
@@ -12,28 +10,10 @@ function redir(to: string) {
);
}
// ── Per-IP rate limiting for auth endpoints ───────────────────────────
const checkAuthRateLimit = createRateLimiter();
function generateKey(): string {
return randomBytes(32).toString("base64url");
}
function hashEmail(email: string): string {
return createHmac("sha256", EMAIL_HMAC_KEY).update(email.toLowerCase().trim()).digest("hex");
}
async function resolveKey(key: string): Promise<{ accountId: string; keyId: string | null; plan: string } | null> {
const [account] = await sql`SELECT id, plan FROM accounts WHERE key = ${key}`;
if (account) return { accountId: account.id, keyId: null, plan: account.plan };
const [apiKey] = await sql`SELECT k.id, k.account_id, a.plan FROM api_keys k JOIN accounts a ON a.id = k.account_id WHERE k.key = ${key}`;
if (apiKey) {
sql`UPDATE api_keys SET last_used_at = now() WHERE id = ${apiKey.id}`.catch(() => {});
return { accountId: apiKey.account_id, keyId: apiKey.id, plan: apiKey.plan };
}
return null;
async function resolveKey(key: string) {
return sharedResolveKey(sql, key);
}
export { resolveKey };
@@ -41,11 +21,7 @@ export { resolveKey };
export function requireAuth(app: Elysia) {
return app
.derive(async ({ headers, cookie, set }) => {
const authHeader = headers["authorization"] ?? "";
const bearer = authHeader.match(/^bearer\s+(.+)$/i)?.[1]?.trim();
const cookieKey = cookie?.pingql_key?.value;
const key = bearer || cookieKey;
const key = extractAuthKey(headers, cookie);
if (!key) {
set.status = 401;
return { accountId: null as string | null, keyId: null as string | null };
@@ -65,15 +41,6 @@ export function requireAuth(app: Elysia) {
});
}
const COOKIE_OPTS = {
httpOnly: true,
secure: process.env.COOKIE_SECURE !== "false",
sameSite: "none" as const,
path: "/",
domain: process.env.COOKIE_DOMAIN ?? ".pingql.com",
maxAge: 60 * 60 * 24 * 30, // 30 days
};
export const account = new Elysia({ prefix: "/account" })
.post("/login", async ({ body, cookie, set, request, error }) => {
+11 -22
View File
@@ -5,8 +5,8 @@ import { resolveKey } from "./auth";
import sql from "../db";
import { sparkline, sparklineFromPings, pickBestRegion } from "../utils/sparkline";
import { createHash } from "crypto";
import { PLAN_LABELS, REGION_COLORS, REGION_LABELS, REGIONS } from "../../../shared/plans";
// Generate a cache-buster hash from the CSS file content at startup
const cssFile = Bun.file(resolve(import.meta.dir, "../dashboard/tailwind.css"));
const cssHash = createHash("md5").update(await cssFile.bytes()).digest("hex").slice(0, 8);
const jsFile = Bun.file(resolve(import.meta.dir, "../dashboard/app.js"));
@@ -23,18 +23,6 @@ function timeAgoSSR(date: string | Date): string {
const sparklineSSR = sparklineFromPings;
const REGION_COLORS: Record<string, string> = {
'eu-central': '#3b82f6', // blue
'us-west': '#f59e0b', // amber
'__none__': '#6b7280', // gray for null region
};
const REGION_LABELS: Record<string, string> = {
'eu-central': '🇩🇪 EU',
'us-west': '🇺🇸 US-W',
'__none__': '?',
};
function latencyChartSSR(pings: any[]): string {
const data = pings.filter((c: any) => c.latency_ms != null);
if (data.length < 2) {
@@ -137,12 +125,6 @@ function escapeHtmlSSR(str: string): string {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
export function html(template: string, data: Record<string, unknown> = {}) {
return new Response(eta.render(template, { ...data, timeAgoSSR, sparklineSSR, pickBestRegion, latencyChartSSR, escapeHtmlSSR, cssHash, jsHash }), {
headers: { "content-type": "text/html; charset=utf-8" },
});
}
function redirect(to: string) {
return new Response(
`<!DOCTYPE html><html lang="en" class="dark"><head><meta charset="UTF-8"><meta http-equiv="refresh" content="0;url=${to}"><meta name="robots" content="noindex"><style>html,body{background:#0a0a0a;margin:0;height:100%}</style></head><body></body></html>`,
@@ -150,10 +132,17 @@ function redirect(to: string) {
);
}
export function html(template: string, data: Record<string, unknown> = {}) {
return new Response(eta.render(template, {
...data, timeAgoSSR, sparklineSSR, pickBestRegion, latencyChartSSR, escapeHtmlSSR, cssHash, jsHash,
regionColors: REGION_COLORS, regionLabels: REGION_LABELS, regions: REGIONS, planLabels: PLAN_LABELS,
}), {
headers: { "content-type": "text/html; charset=utf-8" },
});
}
async function getAccountId(cookie: any, headers: any): Promise<{ accountId: string; keyId: string | null; plan: string } | null> {
const authHeader = headers["authorization"] ?? "";
const bearer = authHeader.match(/^bearer\s+(.+)$/i)?.[1]?.trim();
const key = cookie?.pingql_key?.value || bearer;
const key = cookie?.pingql_key?.value || headers["authorization"]?.match(/^bearer\s+(.+)$/i)?.[1]?.trim();
if (!key) return null;
return await resolveKey(key) ?? null;
}
-21
View File
@@ -1,21 +0,0 @@
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;
};
}
+1 -4
View File
@@ -12,10 +12,7 @@ export function sparkline(values: number[], width = 120, height = 32, color = '#
return `<svg width="${width}" height="${height}" class="inline-block" data-vals="${values.join(',')}" data-region="${region}"><polyline points="${points}" fill="none" stroke="${color}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
}
const REGION_COLORS: Record<string, string> = {
'eu-central': '#3b82f6',
'us-west': '#f59e0b',
};
import { REGION_COLORS } from "../../../shared/plans";
// Pick the best region: the one with the lowest avg latency across its last 3 pings.
// Only considers regions that have at least one ping in the most recent 3 pings overall,
+1 -2
View File
@@ -141,8 +141,7 @@
const mins = Math.floor(remainingSec / 60);
const secs = remainingSec % 60;
const pct = Math.min(100, Math.round((received / total) * 100));
const invPlanNames = { pro: 'Pro', pro2x: 'Pro 2x', pro4x: 'Pro 4x', lifetime: 'Lifetime' };
const invPlanLabel = invPlanNames[inv.plan] || inv.plan;
const invPlanLabel = it.planLabels[inv.plan] || inv.plan;
%>
<% if (isPending) { %>
+3 -10
View File
@@ -476,15 +476,8 @@
});
// ── Interactive latency chart ──────────────────────────────────────
const REGION_COLORS = {
'eu-central': '#3b82f6',
'us-west': '#f59e0b',
'__none__': '#6b7280'
};
const REGION_LABELS = {
'eu-central': 'EU Central',
'us-west': 'US West'
};
const REGION_COLORS = <%~ JSON.stringify(it.regionColors) %>;
const REGION_LABELS = <%~ JSON.stringify(it.regionLabels) %>;
const MAX_RUNS = 100;
const chartPings = <%~ JSON.stringify(chartPings.map(p => ({
@@ -791,7 +784,7 @@
if (regions.length > 0) {
html += '<div class="mt-1.5 pt-1.5 border-t border-gray-700/50">';
for (const r of regions) {
const rLabel = {'eu-central':'EU Central','us-west':'US West'}[r.region] || r.region || 'unknown';
const rLabel = REGION_LABELS[r.region] || r.region || 'unknown';
const status = r.up ? '<span class="text-green-400">Up</span>' : '<span class="text-red-400">Down</span>';
const lat = r.latency_ms != null ? `<span class="text-gray-400 font-mono">${r.latency_ms}ms</span>` : '';
html += `<div class="flex items-center justify-between gap-3"><span>${rLabel}</span><span>${lat} ${status}</span></div>`;
+1 -4
View File
@@ -80,10 +80,7 @@
} catch {}
}, 30000);
const REGION_COLORS = {
'eu-central': '#3b82f6',
'us-west': '#f59e0b',
};
const REGION_COLORS = <%~ JSON.stringify(it.regionColors) %>;
// Per-monitor tracking: regions = {region: [vals]}, timeline = [{region, val}] in arrival order
const sparkData = {}; // mid → { regions: {region: [vals]}, timeline: [{region}] }
+1 -1
View File
@@ -12,7 +12,7 @@
const allIntervals = [['2','2 seconds'],['5','5 seconds'],['10','10 seconds'],['20','20 seconds'],['30','30 seconds'],['60','1 minute'],['300','5 minutes'],['600','10 minutes'],['1800','30 minutes'],['3600','1 hour']];
const intervals = allIntervals.filter(([val]) => Number(val) >= minInterval);
const timeouts = [['5000','5 seconds'],['10000','10 seconds'],['20000','20 seconds'],['30000','30 seconds'],['40000','40 seconds'],['50000','50 seconds'],['60000','60 seconds']];
const regions = [['eu-central','EU Central'],['us-west','US West']];
const regions = it.regions;
const curMethod = monitor.method || 'GET';
const bodyHidden = ['GET','HEAD','OPTIONS'].includes(curMethod);
%>
+3 -5
View File
@@ -5,7 +5,7 @@
const hasEmail = !!it.account.email_hash;
const createdDate = new Date(it.account.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
const plan = it.account.plan || 'free';
const planLabel = { free: 'Free', pro: 'Pro', pro2x: 'Pro 2x', pro4x: 'Pro 4x', lifetime: 'Lifetime' }[plan] || plan;
const planLabel = it.planLabels[plan] || plan;
const limits = {
free: { monitors: 10, interval: '30s', regions: 1 },
pro: { monitors: 200, interval: '5s', regions: 'All' },
@@ -55,9 +55,8 @@
<a href="/dashboard/checkout" class="text-blue-400 hover:text-blue-300 ml-1">Extend or upgrade to Lifetime</a>
<% const stack = typeof it.account.plan_stack === 'string' ? JSON.parse(it.account.plan_stack) : (it.account.plan_stack || []);
if (stack.length > 0) {
const planNames = { free: 'Free', pro: 'Pro', pro2x: 'Pro 2x', pro4x: 'Pro 4x', lifetime: 'Lifetime' };
const parts = stack.map(function(s) {
const name = planNames[s.plan] || s.plan;
const name = it.planLabels[s.plan] || s.plan;
return s.remaining_days == null ? name : name + ' (' + s.remaining_days + 'd)';
});
%>
@@ -176,8 +175,7 @@
const statusColors = { paid: 'green', confirming: 'blue', pending: 'yellow', underpaid: 'orange' };
const statusColor = statusColors[inv.status] || 'gray';
const date = new Date(inv.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
const invPlanNames = { pro: 'Pro', pro2x: 'Pro 2x', pro4x: 'Pro 4x', lifetime: 'Lifetime' };
const planLabel = inv.plan === 'lifetime' ? 'Lifetime' : `${invPlanNames[inv.plan] || 'Pro'} × ${inv.months}mo`;
const planLabel = inv.plan === 'lifetime' ? 'Lifetime' : `${it.planLabels[inv.plan] || 'Pro'} × ${inv.months}mo`;
%>
<div class="flex items-center justify-between p-3 rounded-lg bg-surface border border-border-subtle hover:border-border-strong transition-colors">
<div class="flex items-center gap-3">