From 8e554498f0eed7d67345bbc586b7a7d7ace5247e Mon Sep 17 00:00:00 2001 From: nate Date: Sat, 28 Mar 2026 18:05:29 +0400 Subject: [PATCH] fet: reduce LOC by reducing comments --- apps/api/src/index.ts | 1 - apps/api/src/routes/internal.ts | 8 ---- apps/api/src/routes/monitors.ts | 15 ------- apps/api/src/routes/pings.ts | 10 ----- apps/api/src/utils/ssrf.ts | 4 -- apps/monitor/src/main.rs | 5 +-- apps/monitor/src/query.rs | 53 +------------------------ apps/monitor/src/runner.rs | 39 ------------------ apps/pay/src/address.ts | 3 -- apps/pay/src/index.ts | 3 -- apps/pay/src/monitor.ts | 29 -------------- apps/pay/src/receipt.ts | 2 - apps/pay/src/routes.ts | 13 ------ apps/shared/db.ts | 4 -- apps/shared/plans.ts | 6 --- apps/web/src/dashboard/app.js | 9 ----- apps/web/src/dashboard/query-builder.js | 5 --- apps/web/src/routes/auth.ts | 1 - apps/web/src/routes/dashboard.ts | 32 --------------- apps/web/src/utils/sparkline.ts | 6 --- 20 files changed, 2 insertions(+), 246 deletions(-) diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 20fb822..087898d 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -19,7 +19,6 @@ const elysia = new Elysia() .use(ingest) .use(internal); -// Wrap Elysia with Bun.serve to guarantee CORS + security headers on every response const server = Bun.serve({ port: 3001, async fetch(req) { diff --git a/apps/api/src/routes/internal.ts b/apps/api/src/routes/internal.ts index f029756..dce4218 100644 --- a/apps/api/src/routes/internal.ts +++ b/apps/api/src/routes/internal.ts @@ -1,6 +1,3 @@ -/// Internal endpoints used by the Rust monitor runner. -/// Protected by MONITOR_TOKEN — not exposed to users. - import { Elysia } from "elysia"; import sql from "../db"; import { safeTokenCompare } from "../../../shared/auth"; @@ -10,7 +7,6 @@ export async function pruneOldPings(retentionDays = 90) { return result.count; } -// Run retention cleanup every hour setInterval(() => { const days = Number(process.env.PING_RETENTION_DAYS ?? 90); pruneOldPings(days).catch((err) => console.error("Retention cleanup failed:", err)); @@ -31,9 +27,6 @@ export const internal = new Elysia({ prefix: "/internal", detail: { hide: true } } }) - // Returns monitors due within the next `lookahead_ms` milliseconds (default 2000). - // Nodes receive scheduled_at as an exact unix ms timestamp and sleep until that - // moment before firing — all regions coordinate to the same scheduled slot. .get("/due", async ({ request }) => { const params = new URL(request.url).searchParams; const region = params.get('region') || undefined; @@ -66,7 +59,6 @@ export const internal = new Elysia({ prefix: "/internal", detail: { hide: true } return monitors; }) - // Manual retention cleanup trigger .post("/prune", async () => { const days = Number(process.env.PING_RETENTION_DAYS ?? 90); const deleted = await pruneOldPings(days); diff --git a/apps/api/src/routes/monitors.ts b/apps/api/src/routes/monitors.ts index dce4e6f..243b303 100644 --- a/apps/api/src/routes/monitors.ts +++ b/apps/api/src/routes/monitors.ts @@ -19,37 +19,31 @@ const MonitorBody = t.Object({ export const monitors = new Elysia({ prefix: "/monitors" }) .use(requireAuth) - // List monitors .get("/", async ({ accountId }) => { return sql`SELECT * FROM monitors WHERE account_id = ${accountId} ORDER BY created_at DESC`; }, { detail: { summary: "List monitors", tags: ["monitors"] } }) - // Create monitor .post("/", async ({ accountId, plan, body, set }) => { const limits = getPlanLimits(plan); - // Enforce monitor count limit const [{ count }] = await sql`SELECT COUNT(*)::int as count FROM monitors WHERE account_id = ${accountId}`; if (count >= limits.maxMonitors) { set.status = 403; return { error: `Plan limit reached: ${limits.maxMonitors} monitors (${plan}). Upgrade to create more.` }; } - // Enforce minimum interval for plan const interval = body.interval_s ?? limits.minIntervalS; if (interval < limits.minIntervalS) { set.status = 400; return { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` }; } - // Enforce region limit for plan const regions = body.regions ?? []; if (regions.length > limits.maxRegions) { set.status = 400; return { error: `Free plan allows ${limits.maxRegions} region per monitor. Upgrade to use multi-region.` }; } - // SSRF protection const ssrfError = await validateMonitorUrl(body.url); if (ssrfError) { set.status = 400; return { error: ssrfError }; } const [monitor] = await sql` @@ -69,7 +63,6 @@ export const monitors = new Elysia({ prefix: "/monitors" }) return monitor; }, { body: MonitorBody, detail: { summary: "Create monitor", tags: ["monitors"] } }) - // Get monitor + recent status .get("/:id", async ({ accountId, params, set }) => { const [monitor] = await sql` SELECT * FROM monitors WHERE id = ${params.id} AND account_id = ${accountId} @@ -83,23 +76,19 @@ export const monitors = new Elysia({ prefix: "/monitors" }) return { ...monitor, results }; }, { detail: { summary: "Get monitor with results", tags: ["monitors"] } }) - // Update monitor .patch("/:id", async ({ accountId, plan, params, body, set }) => { const limits = getPlanLimits(plan); - // Enforce minimum interval for plan if (body.interval_s != null && body.interval_s < limits.minIntervalS) { set.status = 400; return { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` }; } - // Enforce region limit for plan if (body.regions && body.regions.length > limits.maxRegions) { set.status = 400; return { error: `Free plan allows ${limits.maxRegions} region per monitor. Upgrade to use multi-region.` }; } - // SSRF protection on URL change if (body.url) { const ssrfError = await validateMonitorUrl(body.url); if (ssrfError) { set.status = 400; return { error: ssrfError }; } @@ -123,7 +112,6 @@ export const monitors = new Elysia({ prefix: "/monitors" }) return monitor; }, { body: t.Partial(MonitorBody), detail: { summary: "Update monitor", tags: ["monitors"] } }) - // Delete monitor .delete("/:id", async ({ accountId, params, set }) => { const [deleted] = await sql` DELETE FROM monitors WHERE id = ${params.id} AND account_id = ${accountId} RETURNING id @@ -132,7 +120,6 @@ export const monitors = new Elysia({ prefix: "/monitors" }) return { deleted: true }; }, { detail: { summary: "Delete monitor", tags: ["monitors"] } }) - // Toggle enabled .post("/:id/toggle", async ({ accountId, params, set }) => { const [monitor] = await sql` UPDATE monitors SET enabled = NOT enabled @@ -143,7 +130,6 @@ export const monitors = new Elysia({ prefix: "/monitors" }) return monitor; }, { detail: { summary: "Toggle monitor on/off", tags: ["monitors"] } }) - // Check history .get("/:id/pings", async ({ accountId, params, query, set }) => { const [monitor] = await sql` SELECT id FROM monitors WHERE id = ${params.id} AND account_id = ${accountId} @@ -169,7 +155,6 @@ export const monitors = new Elysia({ prefix: "/monitors" }) `; } if (filter === "events") { - // State changes: pings where `up` differs from the previous ping's `up` for this monitor return sql` SELECT * FROM ( SELECT *, LAG(up) OVER (ORDER BY checked_at) AS prev_up diff --git a/apps/api/src/routes/pings.ts b/apps/api/src/routes/pings.ts index f743081..0a3c12d 100644 --- a/apps/api/src/routes/pings.ts +++ b/apps/api/src/routes/pings.ts @@ -3,7 +3,6 @@ import sql from "../db"; import { resolveKey } from "./auth"; import { extractAuthKey, safeTokenCompare } from "../../../shared/auth"; -// ── SSE bus ─────────────────────────────────────────────────────────────────── type SSEController = ReadableStreamDefaultController; const bus = new Map>(); // keyed by accountId const enc = new TextEncoder(); @@ -50,22 +49,18 @@ function makeSSEStream(accountId: string): Response { }); } -// ── Routes ──────────────────────────────────────────────────────────────────── export const ingest = new Elysia() - // Internal: called by Rust monitor runner .post("/internal/ingest", async ({ body, headers, set }) => { const token = headers["x-monitor-token"]; if (!safeTokenCompare(token, process.env.MONITOR_TOKEN)) { set.status = 401; return { error: "Unauthorized" }; } - // Validate monitor exists const [monitor_check] = await sql`SELECT id FROM monitors WHERE id = ${body.monitor_id}`; if (!monitor_check) { set.status = 404; return { error: "Monitor not found" }; } const meta = body.meta ? { ...body.meta } : {}; if (body.cert_expiry_days != null) meta.cert_expiry_days = body.cert_expiry_days; - // Extract response body from meta — stored separately const responseBody: string | null = meta.body_preview ?? null; delete meta.body_preview; @@ -91,12 +86,10 @@ export const ingest = new Elysia() RETURNING * `; - // Store response body separately if (responseBody != null && ping) { await sql`INSERT INTO ping_bodies (ping_id, body) VALUES (${ping.id}, ${responseBody})`; } - // Look up account and publish to account-level bus (without body to keep SSE lean) const [monitor] = await sql`SELECT account_id FROM monitors WHERE id = ${body.monitor_id}`; if (monitor) publish(monitor.account_id, ping); @@ -119,7 +112,6 @@ export const ingest = new Elysia() detail: { hide: true }, }) - // Fetch response body for a specific ping .get("/pings/:id/body", async ({ params, headers, cookie, set }) => { const key = extractAuthKey(headers, cookie); if (!key) { set.status = 401; return { error: "Unauthorized" }; } @@ -127,7 +119,6 @@ export const ingest = new Elysia() const resolved = await resolveKey(key); if (!resolved) { set.status = 401; return { error: "Unauthorized" }; } - // Verify the ping belongs to this account const [ping] = await sql` SELECT p.id FROM pings p JOIN monitors m ON m.id = p.monitor_id @@ -139,7 +130,6 @@ export const ingest = new Elysia() return { body: row?.body ?? null }; }, { detail: { hide: true } }) - // SSE: single stream for all of the account's monitors .get("/account/stream", async ({ headers, cookie }) => { const key = extractAuthKey(headers, cookie); if (!key) return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 }); diff --git a/apps/api/src/utils/ssrf.ts b/apps/api/src/utils/ssrf.ts index 43aa65f..f223dd7 100644 --- a/apps/api/src/utils/ssrf.ts +++ b/apps/api/src/utils/ssrf.ts @@ -54,26 +54,22 @@ export async function validateMonitorUrl(url: string): Promise { return "Invalid URL"; } - // Only allow http and https if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { return `Blocked scheme: ${parsed.protocol} — only http: and https: are allowed`; } const hostname = parsed.hostname.toLowerCase(); - // Block localhost by name if (BLOCKED_HOSTNAMES.includes(hostname)) { return "Blocked hostname: localhost is not allowed"; } - // Block non-public TLDs for (const tld of BLOCKED_TLDS) { if (hostname.endsWith(tld)) { return `Blocked TLD: ${tld} is not allowed`; } } - // Resolve DNS and check all IPs try { const ips: string[] = []; try { diff --git a/apps/monitor/src/main.rs b/apps/monitor/src/main.rs index 4040e0d..61fced9 100644 --- a/apps/monitor/src/main.rs +++ b/apps/monitor/src/main.rs @@ -13,10 +13,7 @@ use tracing::{error, info}; #[tokio::main] async fn main() -> Result<()> { - // Install default rustls crypto provider (required for cert expiry checks) - rustls::crypto::ring::default_provider() - .install_default() - .ok(); // ok() — ignore error if already installed + rustls::crypto::ring::default_provider().install_default().ok(); tracing_subscriber::fmt() .with_env_filter(env::var("RUST_LOG").unwrap_or_else(|_| "info".into())) diff --git a/apps/monitor/src/query.rs b/apps/monitor/src/query.rs index c9c76d4..1047a5b 100644 --- a/apps/monitor/src/query.rs +++ b/apps/monitor/src/query.rs @@ -1,38 +1,3 @@ -/// PingQL query evaluation against a check response. -/// -/// Query shape (MongoDB-inspired): -/// -/// Simple equality: -/// { "status": 200 } -/// -/// Operators: -/// { "status": { "$eq": 200 } } -/// { "status": { "$ne": 500 } } -/// { "status": { "$gte": 200, "$lt": 300 } } -/// { "body": { "$contains": "healthy" } } -/// { "body": { "$startsWith": "OK" } } -/// { "body": { "$endsWith": "done" } } -/// { "body": { "$regex": "ok|healthy" } } -/// { "body": { "$exists": true } } -/// { "status": { "$in": [200, 201, 204] } } -/// -/// CSS selector (HTML parsing): -/// { "$select": "span.status", "$eq": "operational" } -/// -/// JSONPath: -/// { "$json": "$.data.status", "$eq": "ok" } -/// -/// Response time: -/// { "$responseTime": { "$lt": 500 } } -/// -/// Certificate expiry: -/// { "$certExpiry": { "$gt": 30 } } -/// -/// Logical: -/// { "$and": [ { "status": 200 }, { "body": { "$contains": "ok" } } ] } -/// { "$or": [ { "status": 200 }, { "status": 204 } ] } -/// { "$not": { "status": 500 } } - use anyhow::{bail, Result}; use regex::Regex; use scraper::{Html, Selector}; @@ -46,11 +11,9 @@ pub struct Response { pub cert_expiry_days: Option, } -/// Returns true if `query` matches `response`. No query = always up. pub fn evaluate(query: &Value, response: &Response) -> Result { match query { Value::Object(map) => { - // $consider — "up" (default) or "down": flips result if conditions match if let Some(consider) = map.get("$consider") { let is_down = consider.as_str() == Some("down"); let rest: serde_json::Map = map.iter() @@ -61,7 +24,6 @@ pub fn evaluate(query: &Value, response: &Response) -> Result { return Ok(if is_down { !matches } else { matches }); } - // $and / $or / $not if let Some(and) = map.get("$and") { let Value::Array(clauses) = and else { bail!("$and expects array") }; return Ok(clauses.iter().all(|c| evaluate(c, response).unwrap_or(false))); @@ -74,19 +36,14 @@ pub fn evaluate(query: &Value, response: &Response) -> Result { return Ok(!evaluate(not, response)?); } - // $responseTime if let Some(cond) = map.get("$responseTime") { let val = Value::Number(serde_json::Number::from(response.latency_ms.unwrap_or(0))); return eval_condition(cond, &val, response); } - - // $certExpiry if let Some(cond) = map.get("$certExpiry") { let val = Value::Number(serde_json::Number::from(response.cert_expiry_days.unwrap_or(0))); return eval_condition(cond, &val, response); } - - // $json — { "$json": { "$.path": { "$op": val } } } if let Some(json_path_map) = map.get("$json") { let path_map = match json_path_map { Value::Object(m) => m, @@ -99,13 +56,11 @@ pub fn evaluate(query: &Value, response: &Response) -> Result { return Ok(true); } - // $select — { "$select": { "css.selector": { "$op": val } } } if let Some(sel_map) = map.get("$select") { let sel_obj = match sel_map { Value::Object(m) => m, _ => bail!("$select expects an object {{ selector: condition }}"), }; - // Parse HTML once for all selectors let doc = Html::parse_document(&response.body); for (selector, condition) in sel_obj { let sel = Selector::parse(selector) @@ -118,7 +73,6 @@ pub fn evaluate(query: &Value, response: &Response) -> Result { return Ok(true); } - // Field-level checks for (field, condition) in map { let field_val = resolve_field(field, response); if !eval_condition(condition, &field_val, response)? { @@ -154,7 +108,6 @@ fn resolve_json_path(body: &str, path: &str) -> Value { if path.is_empty() { return obj; } let mut current = &obj; for part in path.split('.') { - // Handle array indexing like "items[0]" if let Some(idx_start) = part.find('[') { let key = &part[..idx_start]; if !key.is_empty() { @@ -184,7 +137,6 @@ fn resolve_json_path(body: &str, path: &str) -> Value { fn eval_condition(condition: &Value, field_val: &Value, response: &Response) -> Result { match condition { - // Shorthand: { "status": 200 } Value::Number(n) => Ok(field_val.as_f64() == n.as_f64()), Value::String(s) => Ok(field_val.as_str() == Some(s.as_str())), Value::Bool(b) => Ok(field_val.as_bool() == Some(*b)), @@ -242,11 +194,8 @@ fn eval_op(op: &str, field_val: &Value, val: &Value, response: &Response) -> Res } } "$select" => { - // Nested: { "body": { "$select": "css", "$eq": "val" } } let sel_str = val.as_str().unwrap_or(""); - let selected = css_select(&response.body, sel_str); - // If no comparison operator follows, just check existence - selected.is_some() + css_select(&response.body, sel_str).is_some() } _ => { tracing::warn!("Unknown query operator: {op}"); diff --git a/apps/monitor/src/runner.rs b/apps/monitor/src/runner.rs index d83aa7a..fa96843 100644 --- a/apps/monitor/src/runner.rs +++ b/apps/monitor/src/runner.rs @@ -8,7 +8,6 @@ use std::time::Instant; use tokio::sync::Mutex; use tracing::{debug, warn}; -// Cache native root certs per OS thread to avoid reloading from disk on every check. thread_local! { static ROOT_CERTS: Arc>> = Arc::new( rustls_native_certs::load_native_certs() @@ -19,7 +18,6 @@ thread_local! { ); } -/// Fetch due monitors from coordinator, run them, post results back. pub async fn fetch_and_run( client: &reqwest::Client, coordinator_url: &str, @@ -27,8 +25,6 @@ pub async fn fetch_and_run( region: &str, in_flight: &Arc>>, ) -> Result { - // Fetch monitors due within the next 2s — nodes receive exact scheduled_at_ms - // and sleep until that moment, so all regions fire in tight coordination. let url = if region.is_empty() { format!("{coordinator_url}/internal/due?lookahead_ms=2000") } else { @@ -45,12 +41,10 @@ pub async fn fetch_and_run( let n = monitors.len(); if n == 0 { return Ok(0); } - // Shared read-only strings — clone the Arc instead of allocating per monitor let coordinator_url: Arc = Arc::from(coordinator_url); let token: Arc = Arc::from(token); let region: Arc = Arc::from(region); - // Spawn all checks — fire and forget, skip if already in-flight let mut spawned = 0usize; for monitor in monitors { { @@ -66,7 +60,6 @@ pub async fn fetch_and_run( let coordinator_url = Arc::clone(&coordinator_url); let token = Arc::clone(&token); let region_owned = Arc::clone(®ion); - // run_id: hash(monitor_id, interval_bucket) — same across all regions for this window let run_id_owned = { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -77,7 +70,6 @@ pub async fn fetch_and_run( bucket.hash(&mut h); format!("{:016x}", h.finish()) }; - // Convert scheduled_at_ms to an ISO string for storage in the ping let scheduled_at_iso = monitor.scheduled_at_ms.map(|ms| { chrono::DateTime::::from_timestamp_millis(ms) .map(|dt| dt.to_rfc3339()) @@ -85,7 +77,6 @@ pub async fn fetch_and_run( }); let in_flight = in_flight.clone(); tokio::spawn(async move { - // Sleep until the exact scheduled moment — tight multi-region coordination if let Some(ms) = monitor.scheduled_at_ms { let now_ms = chrono::Utc::now().timestamp_millis(); if ms > now_ms { @@ -94,7 +85,6 @@ pub async fn fetch_and_run( } } let timeout_ms = monitor.timeout_ms.unwrap_or(30000); - // Hard deadline: timeout + 5s buffer, so hung checks always resolve let deadline = std::time::Duration::from_millis(timeout_ms + 5000); let result = match tokio::time::timeout(deadline, run_check(&client, &monitor, scheduled_at_iso.clone(), ®ion_owned, &run_id_owned)).await { Ok(r) => r, @@ -113,8 +103,6 @@ pub async fn fetch_and_run( run_id: Some(run_id_owned.clone()), }, }; - // Post result first, then clear in-flight — this prevents the next - // poll from picking up the monitor again before the ping is persisted. if let Err(e) = post_result(&client, &coordinator_url, &token, result).await { warn!("Failed to post result for {}: {e}", monitor.id); } @@ -126,10 +114,7 @@ pub async fn fetch_and_run( } async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Option, region: &str, run_id: &str) -> PingResult { - // Record when the check actually started (used as checked_at in the ping) let checked_at = chrono::Utc::now().to_rfc3339(); - - // Compute jitter: how late we actually started vs when we were scheduled let jitter_ms: Option = scheduled_at.as_deref().and_then(|s| { let scheduled = chrono::DateTime::parse_from_rfc3339(s).ok()?; let now = chrono::Utc::now(); @@ -138,15 +123,10 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op let start = Instant::now(); - // Build request with method, headers, body, timeout let method = monitor.method.as_deref().unwrap_or("GET").to_uppercase(); let timeout = std::time::Duration::from_millis(monitor.timeout_ms.unwrap_or(30000)); let is_https = monitor.url.starts_with("https://"); - // Run the check in a real OS thread using ureq (blocking, synchronous HTTP). - // ureq sets SO_RCVTIMEO/SO_SNDTIMEO at the socket level, which reliably - // interrupts even a hanging TLS handshake — unlike async reqwest which - // cannot cancel syscall-level blocks via future cancellation. let url = monitor.url.clone(); let req_headers = monitor.request_headers.clone(); let req_body = monitor.request_body.clone(); @@ -190,9 +170,6 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op }, Ok((status, headers, body)) => { - // Start cert expiry check in background — don't block result posting. - // We'll use None for cert_expiry_days in query evaluation since it - // shouldn't delay the main result by seconds of extra TLS handshake. let cert_handle = if is_https { let cert_url = monitor.url.clone(); Some(tokio::spawn(async move { @@ -210,9 +187,6 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op let query = &monitor.query; - // Evaluate query if present (cert_expiry_days not yet available — - // $certExpiry queries will use None here; the actual value is - // attached to the result once the background check completes) let (up, query_error) = if let Some(q) = query { let response = Response { status, @@ -225,16 +199,13 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op Ok(result) => (result, None), Err(e) => { warn!("Query error for {}: {e}", monitor.id); - // Fall back to status-based up/down (status < 400, Some(e.to_string())) } } } else { - // Default: up if 2xx/3xx (status < 400, None) }; - // Await the cert check now (it's been running concurrently during query eval) let cert_expiry_days = match cert_handle { Some(h) => h.await.unwrap_or(None), None => None, @@ -265,10 +236,6 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op } } -/// Run an HTTP check synchronously using ureq. -/// ureq applies timeouts at the socket/IO level (not just future cancellation), -/// which reliably interrupts hanging TLS handshakes. -/// Must be called from a std::thread (not async context). fn run_check_blocking( url: &str, method: &str, @@ -276,7 +243,6 @@ fn run_check_blocking( body: Option<&str>, timeout: std::time::Duration, ) -> Result<(u16, HashMap, String), String> { - // Reuse cached root certs (loaded once per OS thread via thread_local) let root_certs = ROOT_CERTS.with(|c| Arc::clone(c)); let tls = ureq::tls::TlsConfig::builder() @@ -352,8 +318,6 @@ fn run_check_blocking( Ok((status, resp_headers, body_out)) } -/// Check SSL certificate expiry for a given HTTPS URL. -/// Returns the number of days until the certificate expires. async fn check_cert_expiry(url: &str) -> Result> { use rustls::ClientConfig; use rustls::pki_types::ServerName; @@ -361,12 +325,10 @@ async fn check_cert_expiry(url: &str) -> Result> { use tokio_rustls::TlsConnector; use x509_parser::prelude::*; - // Parse host and port from URL let url_parsed = reqwest::Url::parse(url)?; let host = url_parsed.host_str().unwrap_or(""); let port = url_parsed.port().unwrap_or(443); - // Build a rustls config that captures certificates let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); @@ -380,7 +342,6 @@ async fn check_cert_expiry(url: &str) -> Result> { let stream = TcpStream::connect(format!("{host}:{port}")).await?; let tls_stream = connector.connect(server_name, stream).await?; - // Get peer certificates let (_, conn) = tls_stream.get_ref(); let certs = conn.peer_certificates().unwrap_or(&[]); diff --git a/apps/pay/src/address.ts b/apps/pay/src/address.ts index 5513b35..e18b70a 100644 --- a/apps/pay/src/address.ts +++ b/apps/pay/src/address.ts @@ -1,6 +1,3 @@ -/// HD address derivation using bitcore-lib family. -/// Derives child addresses at m/0/{index} (external receive chain). - // @ts-ignore import bitcore from "bitcore-lib"; import bs58check from "bs58check"; diff --git a/apps/pay/src/index.ts b/apps/pay/src/index.ts index eedae44..a2fe636 100644 --- a/apps/pay/src/index.ts +++ b/apps/pay/src/index.ts @@ -13,7 +13,6 @@ const app = new Elysia() .onAfterHandle(({ set }) => { Object.assign(set.headers, SECURITY_HEADERS); }) - // CORS for web app .onRequest(({ request, set }) => { const origin = request.headers.get("origin") ?? ""; if (CORS_ORIGIN.includes(origin)) { @@ -42,13 +41,11 @@ const app = new Elysia() console.log(`PingQL Pay running at http://localhost:${app.server?.port}`); -// Run immediately on startup, then every 30 seconds checkPayments().catch((err) => console.error("Payment check failed:", err)); setInterval(() => { checkPayments().catch((err) => console.error("Payment check failed:", err)); }, 30_000); -// Expire pro plans every hour setInterval(() => { expireProPlans().catch((err) => console.error("Plan expiry check failed:", err)); }, 60 * 60_000); diff --git a/apps/pay/src/monitor.ts b/apps/pay/src/monitor.ts index 46ea6b7..65f2a1d 100644 --- a/apps/pay/src/monitor.ts +++ b/apps/pay/src/monitor.ts @@ -1,5 +1,3 @@ -/// Payment monitor: raw SSE + polling fallback. -/// States: pending → underpaid → confirming → paid | expired import sql from "./db"; import { getAddressInfo, getAddressInfoBulk } from "./freedom"; import { COINS, planTier } from "../../shared/plans"; @@ -8,7 +6,6 @@ import { generateReceipt } from "./receipt"; const SOCK_API = process.env.FREEDOM_SOCK ?? "https://sock-v1.freedom.st"; const THRESHOLD = 0.95; -// ── In-memory lookups for SSE matching ────────────────────────────── let addressMap = new Map(); // address → payment let txidToPayment = new Map(); // txid → payment.id const seenTxids = new Set(); @@ -35,8 +32,6 @@ async function refreshMaps() { for (const t of seenTxids) { if (!newTxid.has(t)) seenTxids.delete(t); } } -// ── Core logic: one place for all state transitions ───────────────── - async function recordTx(paymentId: number, address: string, txid: string, amount: number, confirmed: boolean) { // Verify the payment exists and the address matches — prevents stale in-memory state // from attributing transactions to the wrong payment @@ -52,7 +47,6 @@ async function recordTx(paymentId: number, address: string, txid: string, amount ON CONFLICT (payment_id, txid) DO UPDATE SET confirmed = EXCLUDED.confirmed OR payment_txs.confirmed RETURNING (xmax = 0) as is_new `; - // Extend expiry to 24h on new tx if (ins?.is_new) { const exp = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); await sql`UPDATE payments SET expires_at = ${exp} WHERE id = ${paymentId} AND expires_at < ${exp}`; @@ -100,8 +94,6 @@ async function evaluatePayment(paymentId: number) { } } -// ── SSE ───────────────────────────────────────────────────────────── - async function handleTxEvent(event: any) { const txHash = event.data?.tx?.hash; if (!txHash || seenTxids.has(txHash)) return; @@ -179,8 +171,6 @@ async function connectSSE(url: string) { } } -// ── Polling fallback ──────────────────────────────────────────────── - export async function checkPayments() { await sql` UPDATE payments SET status = 'expired' @@ -202,7 +192,6 @@ export async function checkPayments() { if (!info) try { info = await getAddressInfo(payment.address); } catch { continue; } if (!info || info.error) continue; - // Sync txs from address API for (const tx of info.in ?? []) { if (!tx.txid) continue; await recordTx(payment.id, payment.address, tx.txid, Number(tx.amount ?? 0), tx.block != null); @@ -214,21 +203,16 @@ export async function checkPayments() { } } -// ── Helpers ────────────────────────────────────────────────────────── - export function watchPayment(payment: any) { addressMap.set(payment.address, payment); } -// ── Plan stacking logic (pure, testable) ───────────────────────── - interface StackEntry { plan: string; remaining_days: number | null } interface AccountState { plan: string; plan_expires_at: Date | null; plan_stack: StackEntry[] } interface AccountUpdate { plan: string; plan_expires_at: Date | null; plan_stack: StackEntry[] } export function insertIntoStack(stack: StackEntry[], entry: StackEntry): StackEntry[] { const result = stack.slice(); - // Merge if same plan already exists const existing = result.findIndex(e => e.plan === entry.plan); if (existing !== -1) { const old = result[existing]; @@ -237,11 +221,9 @@ export function insertIntoStack(stack: StackEntry[], entry: StackEntry): StackEn } else { result[existing] = { plan: entry.plan, remaining_days: old.remaining_days + entry.remaining_days }; } - // Re-sort after merge result.sort((a, b) => planTier(b.plan) - planTier(a.plan)); return result; } - // Insert at correct position (tier descending) const tier = planTier(entry.plan); let i = 0; while (i < result.length && planTier(result[i].plan) >= tier) i++; @@ -262,19 +244,16 @@ export function computeApplyPlan( const currentIsActive = acc.plan === "lifetime" || (acc.plan !== "free" && currentExpiry && currentExpiry > now); - // No active plan worth saving — just activate the new one if (!currentIsActive || acc.plan === "free") { const expiresAt = newDays != null ? new Date(now.getTime() + newDays * 86400000) : null; return { plan: newPlan, plan_expires_at: expiresAt, plan_stack: stack }; } - // Same plan renewal — extend from current expiry if (newPlan === acc.plan && newDays != null && currentExpiry) { const extended = new Date(currentExpiry.getTime() + newDays * 86400000); return { plan: acc.plan, plan_expires_at: extended, plan_stack: stack }; } - // Upgrade: new plan takes over, current gets frozen onto stack if (planTier(newPlan) > planTier(acc.plan)) { const remainingDays = acc.plan === "lifetime" ? null @@ -284,38 +263,30 @@ export function computeApplyPlan( return { plan: newPlan, plan_expires_at: expiresAt, plan_stack: newStack }; } - // Downgrade/side-grade: purchased plan goes onto stack, current stays active const newStack = insertIntoStack(stack, { plan: newPlan, remaining_days: newDays }); return { plan: acc.plan, plan_expires_at: currentExpiry, plan_stack: newStack }; } export function computeExpiry(acc: AccountState, now: Date): AccountUpdate | null { - // Only expire timed pro plans if (!["pro", "pro2x", "pro4x"].includes(acc.plan)) return null; if (!acc.plan_expires_at || new Date(acc.plan_expires_at) >= now) return null; const stack = (acc.plan_stack || []).slice(); - // Pop layers until we find a valid one or exhaust the stack while (stack.length > 0) { const next = stack.shift()!; if (next.remaining_days === null) { - // Permanent plan (lifetime) return { plan: next.plan, plan_expires_at: null, plan_stack: stack }; } if (next.remaining_days > 0) { const expiresAt = new Date(now.getTime() + next.remaining_days * 86400000); return { plan: next.plan, plan_expires_at: expiresAt, plan_stack: stack }; } - // 0 days — skip, try next } - // Stack exhausted — fall to free return { plan: "free", plan_expires_at: null, plan_stack: [] }; } -// ── DB wrappers ────────────────────────────────────────────────── - async function applyPlan(payment: any) { try { await sql.begin(async (tx) => { diff --git a/apps/pay/src/receipt.ts b/apps/pay/src/receipt.ts index 1517da2..f9c0658 100644 --- a/apps/pay/src/receipt.ts +++ b/apps/pay/src/receipt.ts @@ -5,7 +5,6 @@ export async function generateReceipt(paymentId: number): Promise { const [payment] = await sql`SELECT * FROM payments WHERE id = ${paymentId}`; if (!payment) throw new Error("Payment not found"); - // Already locked — return as-is if (payment.receipt_html) return payment.receipt_html; const coinInfo = COINS[payment.coin]; @@ -126,7 +125,6 @@ export async function generateReceipt(paymentId: number): Promise { `; - // Minify and lock it const minified = html.replace(/\n\s*/g, "").replace(/>\s+<").replace(/\s{2,}/g, " "); await sql`UPDATE payments SET receipt_html = ${minified} WHERE id = ${paymentId}`; return minified; diff --git a/apps/pay/src/routes.ts b/apps/pay/src/routes.ts index 3b49deb..f29c7a4 100644 --- a/apps/pay/src/routes.ts +++ b/apps/pay/src/routes.ts @@ -30,7 +30,6 @@ function requireAuth(app: Elysia) { export const routes = new Elysia() - // Public: available coins and rates .get("/coins", async () => { const [available, rates] = await Promise.all([getAvailableCoins(), getExchangeRates()]); const coins = Object.entries(COINS) @@ -41,13 +40,11 @@ export const routes = new Elysia() .use(requireAuth) - // Create a checkout .post("/checkout", async ({ accountId, keyId, body, set }) => { if (keyId) { set.status = 403; return { error: "Sub-keys cannot create checkouts" }; } const { plan, months, coin } = body; - // Validate plan — block duplicate lifetime if (plan === "lifetime") { const [acc] = await sql`SELECT plan, plan_stack FROM accounts WHERE id = ${accountId}`; const stack = typeof acc.plan_stack === "string" ? JSON.parse(acc.plan_stack) : (acc.plan_stack || []); @@ -55,17 +52,14 @@ export const routes = new Elysia() if (hasLifetime) { set.status = 400; return { error: "You already have a lifetime plan" }; } } - // Validate coin if (!COINS[coin]) { set.status = 400; return { error: `Unknown coin: ${coin}` }; } const available = await getAvailableCoins(); if (!available.includes(coin)) { set.status = 400; return { error: `${coin} is temporarily unavailable` }; } - // Calculate amount const planDef = PLANS[plan]; if (!planDef) { set.status = 400; return { error: `Unknown plan: ${plan}` }; } let amountUsd = planDef.priceUsd ?? (planDef.monthlyUsd! * (months ?? 1)); - // Lifetime discount: credit up to 50% of lifetime price from previous payments if (plan === "lifetime" && planDef.priceUsd) { const [{ total }] = await sql`SELECT COALESCE(SUM(amount_usd), 0)::numeric as total FROM payments WHERE account_id = ${accountId} AND status = 'paid'`; const credit = Math.min(Number(total), planDef.priceUsd * 0.75); @@ -75,15 +69,12 @@ export const routes = new Elysia() const rate = rates[coin]; if (!rate) { set.status = 500; return { error: "Could not fetch exchange rate" }; } - // Crypto amount with 8 decimal precision const amountCrypto = (amountUsd / rate).toFixed(8); - // Get next derivation index for this coin const [{ next_index }] = await sql` SELECT COALESCE(MAX(derivation_index), -1) + 1 as next_index FROM payments WHERE coin = ${coin} `; - // Derive address let address: string; try { address = derive(coin, next_index); @@ -100,10 +91,8 @@ export const routes = new Elysia() RETURNING * `; - // Start watching this address immediately via SSE watchPayment(payment); - // Build payment URI for QR code const coinInfo = COINS[coin]; const uri = `${coinInfo.uri}:${address.replace(/^.*:/, '')}?amount=${amountCrypto}`; @@ -133,7 +122,6 @@ export const routes = new Elysia() }), }) - // Get checkout details .get("/checkout/:id", async ({ accountId, params, set }) => { const [payment] = await sql` SELECT * FROM payments WHERE id = ${params.id} AND account_id = ${accountId} @@ -178,7 +166,6 @@ export const routes = new Elysia() }; }) - // Serve locked receipt for a paid invoice .get("/checkout/:id/receipt", async ({ accountId, params, set }) => { const [payment] = await sql` SELECT * FROM payments WHERE id = ${params.id} AND account_id = ${accountId} diff --git a/apps/shared/db.ts b/apps/shared/db.ts index 8492e36..a7651c3 100644 --- a/apps/shared/db.ts +++ b/apps/shared/db.ts @@ -1,7 +1,6 @@ export async function migrate(sql: any) { await sql`CREATE EXTENSION IF NOT EXISTS pgcrypto`; - // ── Core tables ───────────────────────────────────────────────── await sql` CREATE TABLE IF NOT EXISTS accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), @@ -61,7 +60,6 @@ export async function migrate(sql: any) { ) `; - // ── Column migrations ────────────────────────────────────────── 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 monitors ADD COLUMN IF NOT EXISTS regions TEXT[] NOT NULL DEFAULT '{}'`; @@ -71,11 +69,9 @@ export async function migrate(sql: any) { 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 '[]'`; - // ── Indexes ──────────────────────────────────────────────────── 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)`; - // ── Payment tables ───────────────────────────────────────────── await sql` CREATE TABLE IF NOT EXISTS payments ( id BIGSERIAL PRIMARY KEY, diff --git a/apps/shared/plans.ts b/apps/shared/plans.ts index d2df61b..a7466d1 100644 --- a/apps/shared/plans.ts +++ b/apps/shared/plans.ts @@ -1,4 +1,3 @@ -// ── Types ───────────────────────────────────────────────────────── export type Plan = "free" | "pro" | "pro2x" | "pro4x" | "lifetime"; export interface PlanLimits { @@ -7,7 +6,6 @@ export interface PlanLimits { maxRegions: number; } -// ── Limits ──────────────────────────────────────────────────────── const PLAN_LIMITS: Record = { free: { maxMonitors: 10, minIntervalS: 30, maxRegions: 1 }, pro: { maxMonitors: 200, minIntervalS: 5, maxRegions: 99 }, @@ -20,12 +18,10 @@ export function getPlanLimits(plan: string): PlanLimits { return PLAN_LIMITS[plan as Plan] || PLAN_LIMITS.free; } -// ── Display ─────────────────────────────────────────────────────── export const PLAN_LABELS: Record = { free: "Free", pro: "Pro", pro2x: "Pro 2x", pro4x: "Pro 4x", lifetime: "Lifetime", }; -// ── Pricing ─────────────────────────────────────────────────────── export const PRO_MONTHLY_USD = 12; export const LIFETIME_USD = 140; @@ -45,7 +41,6 @@ export const COINS: Record = { free: 0, pro: 1, lifetime: 1, pro2x: 2, pro4x: 3, }; @@ -54,7 +49,6 @@ export function planTier(plan: string): number { return PLAN_RANK[plan] ?? 0; } -// ── Regions ─────────────────────────────────────────────────────── export const REGION_COLORS: Record = { "eu-central": "#3b82f6", "us-west": "#f59e0b", diff --git a/apps/web/src/dashboard/app.js b/apps/web/src/dashboard/app.js index 89af556..e09bf5e 100644 --- a/apps/web/src/dashboard/app.js +++ b/apps/web/src/dashboard/app.js @@ -1,13 +1,9 @@ -// PingQL Dashboard — shared utilities -// Auth is now cookie-based. No localStorage needed. - const API_BASE = 'https://api.pingql.com'; function logout() { window.location.href = '/dashboard/logout'; } -// requireAuth is a no-op now — server redirects to /dashboard if not authed function requireAuth() { return true; } async function api(path, opts = {}) { @@ -29,7 +25,6 @@ async function api(path, opts = {}) { return data; } -// Format relative time function formatAgo(ms) { const s = Math.ceil(ms / 1000) || 1; if (s < 60) return `${s}s ago`; @@ -44,7 +39,6 @@ function timeAgo(date) { return `${formatAgo(elapsed)}`; } -// Tick all live timestamps setInterval(() => { document.querySelectorAll('.timestamp[data-ts]').forEach(el => { const elapsed = Date.now() - Number(el.dataset.ts); @@ -58,9 +52,6 @@ function escapeHtml(str) { return div.innerHTML; } -// Subscribe to live ping updates for the whole account via a single SSE stream. -// onPing receives each ping object (includes monitor_id). -// Returns an AbortController — call .abort() to close. function watchAccount(onPing) { const ac = new AbortController(); diff --git a/apps/web/src/dashboard/query-builder.js b/apps/web/src/dashboard/query-builder.js index 28de061..3b8705d 100644 --- a/apps/web/src/dashboard/query-builder.js +++ b/apps/web/src/dashboard/query-builder.js @@ -1,5 +1,3 @@ -// PingQL Visual Query Builder - const FIELDS = [ { name: 'status', label: 'Status Code', type: 'number', operators: ['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in'] }, { name: 'body', label: 'Response Body', type: 'string', operators: ['$eq', '$ne', '$contains', '$startsWith', '$endsWith', '$regex', '$exists'] }, @@ -61,7 +59,6 @@ class QueryBuilder { return { [headerField]: { [operator]: parsedVal } }; } if (operator === '$exists') return { [field]: { '$exists': parsedVal } }; - // Simple shorthand for $eq on basic fields if (operator === '$eq') return { [field]: parsedVal }; return { [field]: { [operator]: parsedVal } }; } @@ -93,7 +90,6 @@ class QueryBuilder { return; } - // Strip $consider before parsing rules this.consider = query.$consider === 'down' ? 'down' : 'up'; const q = Object.fromEntries(Object.entries(query).filter(([k]) => k !== '$consider')); @@ -201,7 +197,6 @@ class QueryBuilder { `; - // Bind events this.container.querySelector('#qb-consider').addEventListener('change', (e) => { this.consider = e.target.value; this.render(); diff --git a/apps/web/src/routes/auth.ts b/apps/web/src/routes/auth.ts index bf5d6bb..73ef9fd 100644 --- a/apps/web/src/routes/auth.ts +++ b/apps/web/src/routes/auth.ts @@ -76,7 +76,6 @@ export const account = new Elysia({ prefix: "/account" }) await sql`INSERT INTO accounts (key, email_hash) VALUES (${key}, ${emailHash})`; cookie.pingql_key.set({ value: key, ...COOKIE_OPTS }); - // Form submission → redirect to welcome page showing the key if ((body as any)._form) return redir(`/dashboard/welcome?key=${encodeURIComponent(key)}`); return { key, email_registered: !!emailHash }; diff --git a/apps/web/src/routes/dashboard.ts b/apps/web/src/routes/dashboard.ts index e9252f0..e403394 100644 --- a/apps/web/src/routes/dashboard.ts +++ b/apps/web/src/routes/dashboard.ts @@ -33,7 +33,6 @@ function latencyChartSSR(pings: any[]): string { const pad = { top: 8, bottom: 8 }; const cH = h - pad.top - pad.bottom; - // Build ordered list of unique runs, evenly spaced (matches canvas) const runTimes: Record = {}; for (const p of data) { const rid = p.run_id || p.checked_at; @@ -49,7 +48,6 @@ function latencyChartSSR(pings: any[]): string { runs.forEach((rid, i) => { runIndex[rid] = i; }); const maxIdx = Math.max(runs.length - 1, 1); - // Group by region const byRegion: Record = {}; for (const p of data) { const key = p.region || '__none__'; @@ -71,7 +69,6 @@ function latencyChartSSR(pings: any[]): string { return pad.top + cH - ((v - yMin) / yRange) * cH; } - // Grid lines let grid = ''; for (let i = 0; i <= 4; i++) { const y = (pad.top + (cH / 4) * i).toFixed(1); @@ -152,15 +149,12 @@ const dashDir = resolve(import.meta.dir, "../dashboard"); export const dashboard = new Elysia() .get("/", () => html("landing", {})) - // Shared assets .get("/favicon.svg", () => new Response(Bun.file(`${dashDir}/favicon.svg`), { headers: { "content-type": "image/svg+xml", "cache-control": "public, max-age=86400" } })) .get("/assets/tailwind.css", () => new Response(Bun.file(`${dashDir}/tailwind.css`), { headers: { "cache-control": "public, max-age=31536000, immutable" } })) .get("/assets/app.css", () => new Response(Bun.file(`${dashDir}/app.css`), { headers: { "cache-control": "public, max-age=31536000, immutable" } })) .get("/assets/app.js", () => new Response(Bun.file(`${dashDir}/app.js`), { headers: { "cache-control": "public, max-age=31536000, immutable" } })) - // Dashboard-only assets .get("/dashboard/query-builder.js", () => new Response(Bun.file(`${dashDir}/query-builder.js`), { headers: { "cache-control": "public, max-age=31536000, immutable" } })) - // Login page .get("/dashboard", async ({ cookie }) => { const key = cookie?.pingql_key?.value; if (key) { @@ -172,21 +166,17 @@ export const dashboard = new Elysia() return html("login", {}); }) - // Logout .get("/dashboard/logout", ({ cookie }) => { - // Explicitly expire with same domain/path so browser actually clears it cookie.pingql_key?.set({ value: "", maxAge: 0, path: "/", domain: process.env.COOKIE_DOMAIN ?? ".pingql.com", secure: process.env.NODE_ENV !== "development", sameSite: "lax" }); return redirect("/dashboard"); }) - // Welcome page — shows new account key after registration (no-JS flow) .get("/dashboard/welcome", async ({ cookie, headers, query }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); return html("welcome", { key: query.key || cookie?.pingql_key?.value || "" }); }) - // Home — SSR monitor list .get("/dashboard/home", async ({ cookie, headers }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -202,7 +192,6 @@ export const dashboard = new Elysia() ORDER BY m.created_at DESC `; - // Fetch last 20 pings per monitor for sparklines const monitorIds = monitors.map((m: any) => m.id); let pingsMap: Record = {}; if (monitorIds.length > 0) { @@ -226,7 +215,6 @@ export const dashboard = new Elysia() return html("home", { nav: "monitors", monitors: monitorsWithPings, accountId }); }) - // Settings — SSR account info .get("/dashboard/settings", async ({ cookie, headers }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -239,7 +227,6 @@ export const dashboard = new Elysia() const loginKey = isSubKey ? null : (cookie?.pingql_key?.value ?? null); const [{ count: monitorCount }] = await sql`SELECT COUNT(*)::int as count FROM monitors WHERE account_id = ${accountId}`; - // Fetch paid + active (non-expired) invoices let invoices: any[] = []; try { invoices = await sql` @@ -255,7 +242,6 @@ export const dashboard = new Elysia() return html("settings", { nav: "settings", account: acc, apiKeys, accountId, loginKey, isSubKey, monitorCount, invoices }); }) - // Checkout — upgrade plan .get("/dashboard/checkout", async ({ cookie, headers }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -264,10 +250,8 @@ export const dashboard = new Elysia() const hasLifetime = acc.plan === "lifetime" || stack.some((s: any) => s.plan === "lifetime"); if (acc.plan === "lifetime" && stack.length === 0) return redirect("/dashboard/settings"); - // Total spent on paid invoices (for lifetime discount) const [{ total_spent }] = await sql`SELECT COALESCE(SUM(amount_usd), 0)::numeric as total_spent FROM payments WHERE account_id = ${resolved.accountId} AND status = 'paid'`; - // Fetch coins server-side for no-JS rendering const payApi = process.env.PAY_API || "https://pay.pingql.com"; let coins: any[] = []; try { @@ -279,7 +263,6 @@ export const dashboard = new Elysia() return html("checkout", { nav: "settings", account: acc, payApi, invoiceId: null, coins, invoice: null, totalSpent: Number(total_spent), hasLifetime }); }) - // Existing invoice by ID — SSR the payment status .get("/dashboard/checkout/:id", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -302,7 +285,6 @@ export const dashboard = new Elysia() return html("checkout", { nav: "settings", account: acc, payApi, invoiceId: params.id, coins, invoice }); }) - // Receipt (proxy to pay service, serves HTML directly) .get("/dashboard/checkout/:id/receipt", async ({ cookie, headers, params, set }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -326,7 +308,6 @@ export const dashboard = new Elysia() } }) - // Create checkout via form POST (no-JS) .post("/dashboard/checkout", async ({ cookie, headers, body }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -352,7 +333,6 @@ export const dashboard = new Elysia() return redirect("/dashboard/checkout"); }) - // New monitor .get("/dashboard/monitors/new", async ({ cookie, headers }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -361,7 +341,6 @@ export const dashboard = new Elysia() return html("new", { nav: "monitors", plan: resolved?.plan || "free" }); }) - // Home data endpoint for polling (monitor list change detection) .get("/dashboard/home/data", async ({ cookie, headers }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -376,7 +355,6 @@ export const dashboard = new Elysia() }); }) - // Monitor detail — SSR with initial data .get("/dashboard/monitors/:id", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -396,7 +374,6 @@ export const dashboard = new Elysia() return html("detail", { nav: "monitors", monitor, pings, plan: resolved?.plan || "free" }); }) - // Chart partial endpoint — returns just the latency chart SVG .get("/dashboard/monitors/:id/chart", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -418,7 +395,6 @@ export const dashboard = new Elysia() }); }) - // Sparkline partial — returns just the SVG for one monitor .get("/dashboard/monitors/:id/sparkline", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); const accountId = resolved?.accountId ?? null; @@ -440,9 +416,6 @@ export const dashboard = new Elysia() }); }) - // ── Form-based monitor actions (no-JS support) ───────────────────── - - // Create monitor via form POST .post("/dashboard/monitors/new", async ({ cookie, headers, body, set }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -451,7 +424,6 @@ export const dashboard = new Elysia() const regions = Array.isArray(b.regions) ? b.regions : (b.regions ? [b.regions] : []); const query = b.query ? (typeof b.query === "string" ? JSON.parse(b.query) : b.query) : undefined; const requestHeaders: Record = {}; - // Collect header_key[]/header_value[] pairs const hKeys = Array.isArray(b.header_key) ? b.header_key : (b.header_key ? [b.header_key] : []); const hVals = Array.isArray(b.header_value) ? b.header_value : (b.header_value ? [b.header_value] : []); for (let i = 0; i < hKeys.length; i++) { @@ -481,7 +453,6 @@ export const dashboard = new Elysia() return redirect("/dashboard/home"); }) - // Edit monitor via form POST .post("/dashboard/monitors/:id/edit", async ({ cookie, headers, params, body }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -519,7 +490,6 @@ export const dashboard = new Elysia() return redirect(`/dashboard/monitors/${params.id}`); }) - // Delete monitor via form POST .post("/dashboard/monitors/:id/delete", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -534,7 +504,6 @@ export const dashboard = new Elysia() return redirect("/dashboard/home"); }) - // Toggle monitor via form POST .post("/dashboard/monitors/:id/toggle", async ({ cookie, headers, params }) => { const resolved = await getAccountId(cookie, headers); if (!resolved?.accountId) return redirect("/dashboard"); @@ -549,7 +518,6 @@ export const dashboard = new Elysia() return redirect(`/dashboard/monitors/${params.id}`); }) - // Docs .get("/docs", () => html("docs", {})) .get("/privacy", () => html("privacy", {})) .get("/terms", () => html("tos", {})); diff --git a/apps/web/src/utils/sparkline.ts b/apps/web/src/utils/sparkline.ts index ce9703b..c34f1f6 100644 --- a/apps/web/src/utils/sparkline.ts +++ b/apps/web/src/utils/sparkline.ts @@ -14,14 +14,10 @@ export function sparkline(values: number[], width = 120, height = 32, color = '# 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, -// so stale regions that haven't reported recently are excluded. export function pickBestRegion(pings: Array<{latency_ms?: number|null, region?: string|null}>): { region: string, values: number[], latest: number | null } { const withLatency = pings.filter(p => p.latency_ms != null); if (!withLatency.length) return { region: '__none__', values: [], latest: null }; - // Group all pings by region const byRegion: Record = {}; for (const p of withLatency) { const key = p.region || '__none__'; @@ -29,7 +25,6 @@ export function pickBestRegion(pings: Array<{latency_ms?: number|null, region?: byRegion[key].push(p.latency_ms!); } - // Only consider regions that appear in the 3 most recent pings const recentRegions = new Set( withLatency.slice(-3).map(p => p.region || '__none__') ); @@ -47,7 +42,6 @@ export function pickBestRegion(pings: Array<{latency_ms?: number|null, region?: return { region: bestRegion, values, latest: values.length ? values[values.length - 1] : null }; } -// Given pings with region+latency, pick the best region and render its sparkline. export function sparklineFromPings(pings: Array<{latency_ms?: number|null, region?: string|null}>, width = 120, height = 32): string { const { region, values } = pickBestRegion(pings); if (!values.length) return '';