fix(webapp): derive expiry status from loader time

This commit is contained in:
Chris Arderne
2026-08-19 17:31:47 +01:00
parent 32d97af360
commit 465f52bd44
2 changed files with 37 additions and 18 deletions
@@ -173,6 +173,7 @@ export const loader = dashboardLoader(
additionalApiKeyIssuanceEnabled,
isRbacPluginAvailable,
showRevoked: searchParams.showRevoked ?? false,
loadedAt: Date.now(),
});
} catch (error) {
console.error(error);
@@ -308,6 +309,7 @@ export default function Page() {
hasVercelIntegration,
availableTasks,
presets,
loadedAt,
} = useTypedLoaderData<typeof loader>();
const apiKeyEnvironmentLabel = {
@@ -398,7 +400,7 @@ export default function Page() {
</div>
</TableCell>
<TableCell>
<ApiKeyStatus />
<ApiKeyStatus now={loadedAt} />
</TableCell>
<TableCell>
<ApiKeyAccess label="No restrictions" />
@@ -424,7 +426,7 @@ export default function Page() {
{apiKeys.map((apiKey) => {
const isExpired = apiKey.expiresAt
? new Date(apiKey.expiresAt).getTime() <= Date.now()
? new Date(apiKey.expiresAt).getTime() <= loadedAt
: false;
const cannotAuthenticate = Boolean(apiKey.revokedAt) || isExpired;
const cannotRevoke = Boolean(apiKey.revokedAt) || isExpired;
@@ -445,7 +447,11 @@ export default function Page() {
<span className="font-mono text-text-dimmed">{apiKey.obfuscated}</span>
</TableCell>
<TableCell>
<ApiKeyStatus revokedAt={apiKey.revokedAt} expiresAt={apiKey.expiresAt} />
<ApiKeyStatus
revokedAt={apiKey.revokedAt}
expiresAt={apiKey.expiresAt}
now={loadedAt}
/>
</TableCell>
<TableCell>
<ApiKeyAccess
@@ -1338,9 +1344,11 @@ function ApiKeyAccess({
function ApiKeyStatus({
revokedAt,
expiresAt,
now,
}: {
revokedAt?: Date | string | null;
expiresAt?: Date | string | null;
now: number;
}) {
if (revokedAt) {
return (
@@ -1351,7 +1359,7 @@ function ApiKeyStatus({
);
}
if (expiresAt && new Date(expiresAt).getTime() <= Date.now()) {
if (expiresAt && new Date(expiresAt).getTime() <= now) {
return (
<div className="flex items-center gap-1 text-xs text-text-dimmed">
<ExclamationTriangleIcon className="size-4" />
@@ -115,21 +115,18 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
throw new Response("Session not found", { status: 404 });
}
return typedjson({ session });
return typedjson({ session, loadedAt: Date.now() });
};
export default function Page() {
const { session } = useTypedLoaderData<typeof loader>();
const { session, loadedAt } = useTypedLoaderData<typeof loader>();
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
const isExpired = session.expiresAt != null && new Date(session.expiresAt).getTime() < loadedAt;
const status: SessionStatus =
session.closedAt != null
? "CLOSED"
: session.expiresAt != null && new Date(session.expiresAt).getTime() < Date.now()
? "EXPIRED"
: "ACTIVE";
session.closedAt != null ? "CLOSED" : isExpired ? "EXPIRED" : "ACTIVE";
const displayId = session.externalId ?? session.friendlyId;
const sessionsPath = v3SessionsPath(organization, project, environment);
@@ -170,7 +167,7 @@ export default function Page() {
default="420px"
className="overflow-hidden"
>
<InspectorPane session={session} status={status} />
<InspectorPane session={session} status={status} isExpired={isExpired} />
</ResizablePanel>
</ResizablePanelGroup>
</PageBody>
@@ -712,7 +709,15 @@ function MergedStreamRow({
);
}
function InspectorPane({ session, status }: { session: LoadedSession; status: SessionStatus }) {
function InspectorPane({
session,
status,
isExpired,
}: {
session: LoadedSession;
status: SessionStatus;
isExpired: boolean;
}) {
const { value, replace } = useSearchParams();
const tab = value("tab") ?? "overview";
const organization = useOrganization();
@@ -761,7 +766,7 @@ function InspectorPane({ session, status }: { session: LoadedSession; status: Se
</div>
<div className="overflow-y-auto px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
{tab === "overview" ? (
<OverviewTab session={session} status={status} />
<OverviewTab session={session} status={status} isExpired={isExpired} />
) : tab === "runs" ? (
<RunsTab session={session} status={status} allRunsPath={allRunsPath} />
) : (
@@ -772,7 +777,15 @@ function InspectorPane({ session, status }: { session: LoadedSession; status: Se
);
}
function OverviewTab({ session, status }: { session: LoadedSession; status: SessionStatus }) {
function OverviewTab({
session,
status,
isExpired,
}: {
session: LoadedSession;
status: SessionStatus;
isExpired: boolean;
}) {
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
@@ -892,9 +905,7 @@ function OverviewTab({ session, status }: { session: LoadedSession; status: Sess
</Property.Item>
{session.expiresAt ? (
<Property.Item>
<Property.Label>
{new Date(session.expiresAt).getTime() < Date.now() ? "Expired" : "Expires"}
</Property.Label>
<Property.Label>{isExpired ? "Expired" : "Expires"}</Property.Label>
<Property.Value>
<DateTime date={session.expiresAt} />
</Property.Value>