Skip to content

v2.3.0#103

Merged
byteful merged 31 commits into
mainfrom
dev
Jul 9, 2026
Merged

v2.3.0#103
byteful merged 31 commits into
mainfrom
dev

Conversation

@byteful

@byteful byteful commented Jul 9, 2026

Copy link
Copy Markdown
Member

Confidence Score: 4/5

This PR has one contained user-facing bug that should be fixed before relying on the new rate-limit page.

The score reflects a single blocking issue in the 429 recovery flow. Other reviewed realtime, dashboard, upload, localization, and build changes appear generally contained.

client/src/lib/api.ts, client/src/utils/rate-limit-handler.ts, client/src/pages/RateLimitPage.tsx

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced the rate-limit bypass by running a focused Vite SSR harness that imports the actual apiFetch module and feeds it a mocked 429 response, confirming the 429 path raises Rate limit exceeded and that after handling sessionStorage.rateLimitInfo and sessionStorage.preRateLimitPath are null, the pathname remains /dashboard, and /rate-limit is not reached.
  • Validated the rate-limit UI by rendering the real app’s /rate-limit view, which shows Rate Limit Exceeded with the countdown and details, a security notice, a disabled wait button, and a Return to Dashboard action; the accompanying logs indicate the /rate-limit URL and the post-click navigation state toward the unauthenticated panel auth flow.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
client/src/lib/api.ts Updates API fetch rate-limit handling, but the new handler no longer routes users to the added rate-limit page.
client/src/utils/rate-limit-handler.ts Simplifies 429 handling to a toast-only flow, leaving the new rate-limit page without stored state or navigation.
client/src/pages/RateLimitPage.tsx Adds a rate-limit recovery page that depends on sessionStorage state which is no longer populated by the 429 handler.
client/src/App.tsx Adds realtime invalidation, language preference syncing, rate-limit route wiring, and more resilient public-settings error handling.
client/src/hooks/use-realtime.tsx Introduces WebSocket-based realtime invalidation with topic-scoped query invalidations.
client/src/components/dashboard/DashboardAlertsSection.tsx Adds dashboard system alert rendering with severity-to-banner mapping and markdown message display.
client/src/hooks/data/dashboard.ts Moves dashboard queries into a feature data hook module and adds system alerts query support.
client/src/pages/home.tsx Refactors dashboard loading and refresh behavior to include alerts alongside existing ticket and punishment sections.
client/src/components/MediaUpload.tsx Improves upload state safety with unmount guards, better file-cap accounting, and typed metadata.
client/src/components/TicketAttachments.tsx Adds delete confirmations/loading state and centralizes trusted CDN URL checks for attachment previews.
.github/workflows/build.yml Switches CI dependency installation from GitHub Packages to the Nexus npm registry.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
client/src/lib/api.ts:69-74
**Rate limit page bypassed**
`handleRateLimitIfNeeded` now only calls `handleRateLimitResponse(response)` and throws, while the newly routed `/rate-limit` page reads `rateLimitInfo` and `preRateLimitPath` from `sessionStorage`. No changed code writes those keys or navigates to `/rate-limit`, so a `429` response never reaches the countdown/retry page and users only get a transient toast instead of the intended recovery UI.

Reviews (2): Last reviewed commit: "fix realtime/websocket issue" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

  • Rule used - This is a React frontend project on React 19 with ... (source)

Comment thread client/src/hooks/use-realtime.tsx Outdated
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploying modl-panel with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7a93a07
Status: ✅  Deploy successful!
Preview URL: https://8d424a60.staging-panel.pages.dev
Branch Preview URL: https://dev.staging-panel.pages.dev

View logs

Comment thread client/src/lib/api.ts
Comment on lines 69 to 74
async function handleRateLimitIfNeeded(response: Response): Promise<void> {
if (response.status === 429) {
const { handleRateLimitResponse, getCurrentPath } = await import('../utils/rate-limit-handler');
await handleRateLimitResponse(response, getCurrentPath());
const { handleRateLimitResponse } = await import('../utils/rate-limit-handler');
await handleRateLimitResponse(response);
throw new Error('Rate limit exceeded');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Rate limit page bypassed
handleRateLimitIfNeeded now only calls handleRateLimitResponse(response) and throws, while the newly routed /rate-limit page reads rateLimitInfo and preRateLimitPath from sessionStorage. No changed code writes those keys or navigates to /rate-limit, so a 429 response never reaches the countdown/retry page and users only get a transient toast instead of the intended recovery UI.

Rule Used: This is a React frontend project on React 19 with ... (source)

Artifacts

Repro: focused Node/Vite harness invoking the actual apiFetch 429 path

  • Contains supporting evidence from the run (text/javascript; charset=utf-8).

Repro: toast capture shim used by the harness

  • Contains supporting evidence from the run (text/javascript; charset=utf-8).

Repro: command output showing unchanged route, empty rate-limit sessionStorage keys, and toast-only handling

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: client/src/lib/api.ts
Line: 69-74

Comment:
**Rate limit page bypassed**
`handleRateLimitIfNeeded` now only calls `handleRateLimitResponse(response)` and throws, while the newly routed `/rate-limit` page reads `rateLimitInfo` and `preRateLimitPath` from `sessionStorage`. No changed code writes those keys or navigates to `/rate-limit`, so a `429` response never reaches the countdown/retry page and users only get a transient toast instead of the intended recovery UI.

**Rule Used:** This is a React frontend project on React 19 with ... ([source](https://app.greptile.com/modl-gg/-/custom-context?memory=b7532101-0c9e-4ab6-b168-353a105ba593))

How can I resolve this? If you propose a fix, please make it concise.

@byteful byteful merged commit b0faf2f into main Jul 9, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant