Skip to content

Commit 87dc09a

Browse files
committed
fix(webapp): fall back to the default window for unusable queue metric periods
A hand-edited `?period=garbage` or `?period=9999d` was passed straight to the metric queries, so the charts and the SSR'd table could end up on different windows. Period resolution now rejects anything the picker could not produce (including windows past the 30 day retention), and the queues list loader resolves through the same path the client queries use.
1 parent b5bca95 commit 87dc09a

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

apps/webapp/app/components/queues/queueMetricsPeriod.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import parse from "parse-duration";
12
import { useEffect } from "react";
23

34
/**
@@ -17,8 +18,13 @@ const COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
1718
/** The shape TimeFilter writes: a count plus a minute/hour/day unit (presets and custom durations). */
1819
const PERIOD_PATTERN = /^\d{1,4}[mhd]$/;
1920

21+
/** Queue metrics are retained for 30 days, so a longer window can only ever render empty. */
22+
const MAX_PERIOD_MS = 30 * 24 * 60 * 60 * 1000;
23+
2024
function isPeriod(value: string | undefined | null): value is string {
21-
return typeof value === "string" && PERIOD_PATTERN.test(value);
25+
if (typeof value !== "string" || !PERIOD_PATTERN.test(value)) return false;
26+
const ms = parse(value);
27+
return typeof ms === "number" && ms > 0 && ms <= MAX_PERIOD_MS;
2228
}
2329

2430
/** Loader side: the remembered period, falling back to the default when nothing usable is stored. */
@@ -50,8 +56,12 @@ export function useRememberQueueMetricsPeriod(period: string | undefined) {
5056
}
5157

5258
/**
53-
* The window the page should show: an explicit period wins, an absolute range means "no period",
54-
* and everything else falls back to the remembered default the loader resolved.
59+
* The window the page should show: a usable period in the URL wins, an absolute range means "no
60+
* period", and everything else (including a period the picker could never produce, e.g. a
61+
* hand-edited `?period=garbage`) falls back to the remembered default the loader resolved.
62+
*
63+
* Both the loaders and the client-side chart queries resolve through here, so they can't disagree
64+
* about the window.
5565
*/
5666
export function resolveQueueMetricsPeriod({
5767
period,
@@ -64,7 +74,7 @@ export function resolveQueueMetricsPeriod({
6474
to: string | undefined;
6575
defaultPeriod: string;
6676
}): string | null {
67-
if (period) return period;
77+
if (isPeriod(period)) return period;
6878
if (from || to) return null;
6979
return defaultPeriod;
7080
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
201201
q.type === "task" ? `task/${q.name}` : q.name
202202
);
203203
const timeRange = timeFilterFromTo({
204-
period,
204+
period: resolveQueueMetricsPeriod({ period, from, to, defaultPeriod }) ?? undefined,
205205
from: parseFiniteInt(from),
206206
to: parseFiniteInt(to),
207207
defaultPeriod,

0 commit comments

Comments
 (0)