From 0221827ba944a559b76b0135c31a95ec90d96606 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 23 Jan 2026 10:17:13 +0000 Subject: [PATCH] fix(dashboard): display correct batch rate limit current token value --- .../presenters/v3/LimitsPresenter.server.ts | 41 +++++++++++++++++-- .../route.tsx | 1 + 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/presenters/v3/LimitsPresenter.server.ts b/apps/webapp/app/presenters/v3/LimitsPresenter.server.ts index a5ffa3b6ee..11b66d6c0b 100644 --- a/apps/webapp/app/presenters/v3/LimitsPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/LimitsPresenter.server.ts @@ -83,10 +83,12 @@ export class LimitsPresenter extends BasePresenter { public async call({ organizationId, projectId, + environmentId, environmentApiKey, }: { organizationId: string; projectId: string; + environmentId: string; environmentApiKey: string; }): Promise { // Get organization with all limit-related fields @@ -159,9 +161,9 @@ export class LimitsPresenter extends BasePresenter { environmentApiKey, apiRateLimitConfig ); - const batchRateLimitTokens = await getRateLimitRemainingTokens( - "batch", - environmentApiKey, + // Batch rate limiter uses environment ID directly (not hashed) with a different key prefix + const batchRateLimitTokens = await getBatchRateLimitRemainingTokens( + environmentId, batchRateLimitConfig ); @@ -419,3 +421,36 @@ async function getRateLimitRemainingTokens( return null; } } + +/** + * Query the current remaining tokens for the batch rate limiter. + * The batch rate limiter uses environment ID directly (not hashed) and has a different key prefix. + */ +async function getBatchRateLimitRemainingTokens( + environmentId: string, + config: RateLimiterConfig +): Promise { + try { + // Create a Ratelimit instance with the same configuration as the batch rate limiter + const limiter = createLimiterFromConfig(config); + const ratelimit = new Ratelimit({ + redis: rateLimitRedisClient, + limiter, + ephemeralCache: new Map(), + analytics: false, + // The batch rate limiter uses "ratelimit:batch" as keyPrefix in RateLimiter, + // which adds another "ratelimit:" prefix, resulting in "ratelimit:ratelimit:batch" + prefix: `ratelimit:ratelimit:batch`, + }); + + // Batch rate limiter uses environment ID directly (not hashed) + const remaining = await ratelimit.getRemaining(environmentId); + return remaining; + } catch (error) { + logger.warn("Failed to get batch rate limit remaining tokens", { + environmentId, + error: error instanceof Error ? error.message : String(error), + }); + return null; + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx index 1ca8778dba..dfaffe9938 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.limits/route.tsx @@ -81,6 +81,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { presenter.call({ organizationId: project.organizationId, projectId: project.id, + environmentId: environment.id, environmentApiKey: environment.apiKey, }) );