feat: multi-region monitor support — region selector in UI, region flag on pings
This commit is contained in:
@@ -33,9 +33,10 @@ export const internal = new Elysia({ prefix: "/internal", detail: { hide: true }
|
||||
|
||||
// Returns monitors that are due for a check.
|
||||
// scheduled_at = last_checked_at + interval_s (ideal fire time), so jitter = actual_start - scheduled_at
|
||||
.get("/due", async () => {
|
||||
.get("/due", async ({ query }) => {
|
||||
const region = query.region as string | undefined;
|
||||
const monitors = await sql`
|
||||
SELECT m.id, m.url, m.method, m.request_headers, m.request_body, m.timeout_ms, m.interval_s, m.query,
|
||||
SELECT m.id, m.url, m.method, m.request_headers, m.request_body, m.timeout_ms, m.interval_s, m.query, m.regions,
|
||||
CASE
|
||||
WHEN last.checked_at IS NULL THEN now()
|
||||
ELSE last.checked_at + (m.interval_s || ' seconds')::interval
|
||||
@@ -49,6 +50,11 @@ export const internal = new Elysia({ prefix: "/internal", detail: { hide: true }
|
||||
WHERE m.enabled = true
|
||||
AND (last.checked_at IS NULL
|
||||
OR last.checked_at < now() - (m.interval_s || ' seconds')::interval)
|
||||
AND (
|
||||
array_length(m.regions, 1) IS NULL
|
||||
OR m.regions = '{}'
|
||||
OR ${region ? sql`${region} = ANY(m.regions)` : sql`true`}
|
||||
)
|
||||
`;
|
||||
return monitors;
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ const MonitorBody = t.Object({
|
||||
timeout_ms: t.Optional(t.Number({ minimum: 1000, maximum: 60000, default: 30000, description: "Request timeout in ms" })),
|
||||
interval_s: t.Optional(t.Number({ minimum: 1, default: 60, description: "Check interval in seconds" })),
|
||||
query: t.Optional(t.Any({ description: "PingQL query — filter conditions for up/down" })),
|
||||
regions: t.Optional(t.Array(t.String(), { description: "Regions to run checks from. Empty array = all regions." })),
|
||||
});
|
||||
|
||||
export const monitors = new Elysia({ prefix: "/monitors" })
|
||||
@@ -28,8 +29,9 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
||||
const ssrfError = await validateMonitorUrl(body.url);
|
||||
if (ssrfError) return error(400, { error: ssrfError });
|
||||
|
||||
const regions = body.regions ?? [];
|
||||
const [monitor] = await sql`
|
||||
INSERT INTO monitors (account_id, name, url, method, request_headers, request_body, timeout_ms, interval_s, query)
|
||||
INSERT INTO monitors (account_id, name, url, method, request_headers, request_body, timeout_ms, interval_s, query, regions)
|
||||
VALUES (
|
||||
${accountId}, ${body.name}, ${body.url},
|
||||
${(body.method ?? 'GET').toUpperCase()},
|
||||
@@ -37,7 +39,8 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
||||
${body.request_body ?? null},
|
||||
${body.timeout_ms ?? 30000},
|
||||
${body.interval_s ?? 60},
|
||||
${body.query ? sql.json(body.query) : null}
|
||||
${body.query ? sql.json(body.query) : null},
|
||||
${sql.array(regions)}
|
||||
)
|
||||
RETURNING *
|
||||
`;
|
||||
@@ -75,7 +78,8 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
||||
request_body = COALESCE(${body.request_body ?? null}, request_body),
|
||||
timeout_ms = COALESCE(${body.timeout_ms ?? null}, timeout_ms),
|
||||
interval_s = COALESCE(${body.interval_s ?? null}, interval_s),
|
||||
query = COALESCE(${body.query ? sql.json(body.query) : null}, query)
|
||||
query = COALESCE(${body.query ? sql.json(body.query) : null}, query),
|
||||
regions = COALESCE(${body.regions ? sql.array(body.regions) : null}, regions)
|
||||
WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
@@ -73,7 +73,7 @@ export const ingest = new Elysia()
|
||||
const jitterMs = body.jitter_ms ?? null;
|
||||
|
||||
const [ping] = await sql`
|
||||
INSERT INTO pings (monitor_id, scheduled_at, jitter_ms, status_code, latency_ms, up, error, meta)
|
||||
INSERT INTO pings (monitor_id, scheduled_at, jitter_ms, status_code, latency_ms, up, error, meta, region)
|
||||
VALUES (
|
||||
${body.monitor_id},
|
||||
${scheduledAt},
|
||||
@@ -82,7 +82,8 @@ export const ingest = new Elysia()
|
||||
${body.latency_ms ?? null},
|
||||
${body.up},
|
||||
${body.error ?? null},
|
||||
${Object.keys(meta).length > 0 ? sql.json(meta) : null}
|
||||
${Object.keys(meta).length > 0 ? sql.json(meta) : null},
|
||||
${body.region ?? null}
|
||||
)
|
||||
RETURNING *
|
||||
`;
|
||||
@@ -103,6 +104,7 @@ export const ingest = new Elysia()
|
||||
error: t.Optional(t.Nullable(t.String())),
|
||||
cert_expiry_days: t.Optional(t.Nullable(t.Number())),
|
||||
meta: t.Optional(t.Any()),
|
||||
region: t.Optional(t.Nullable(t.String())),
|
||||
}),
|
||||
detail: { hide: true },
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user