Engineering pass: fix polling leak, security hardening, and migrate to Next 16 conventions - #1
Open
arena-ai-coding-agent[bot] wants to merge 1 commit into
Open
Conversation
… to Next 16 conventions Critical bug fixes: - roblox-panel.tsx: fix polling interval leak (effect deps caused multiple intervals to stack). Now uses single setInterval with proper cleanup; uses refs to avoid effect re-creation. - history-list.tsx: same pattern — ref-based polling that doesn't recreate on every state update. - roblox-api.ts: verifyApiKey was a no-op (404 from a non-existent endpoint was treated as 'valid'). Now probes a real asset endpoint and uses 401/403 vs 200/404 to discriminate. - audio-processor.ts: getWaveform was returning Math.random() on ffmpeg failure, masking broken files. Now returns empty array so the UI can show a clear 'no waveform' state. - audio-processor.ts: ensureDirs moved off module top-level to prevent import-time side effects on read-only filesystems and to avoid Turbopack NFT issues. All UPLOAD_DIR/PROCESSED_DIR uses replaced with lazy getters. Security: - store.ts: Roblox API key is NO LONGER persisted to localStorage. Identity is remembered; the user must re-verify with their key before any upload. Includes one-time migration that scrubs legacy keys from existing localStorage entries. - /api/roblox/status: API key moved from URL query string to request body (POST) or X-Roblox-Api-Key header. The key is no longer logged in reverse-proxy access logs or browser history. - /api/roblox/verify and /api/roblox/upload: added lightweight in-memory rate limiting (10 req/min for verify, 20 req/min for upload) to prevent trivial DoS and Roblox API throttling. - /api/roblox/upload: explicit 100 MB cap on in-memory audio buffer with proper 413 response. Next.js 16 migration: - src/middleware.ts → src/proxy.ts: Next 16 deprecated the middleware file convention in favor of proxy (same semantics). - next.config.ts: reactStrictMode enabled to catch effect bugs; ignoreBuildErrors kept for legacy reasons. Removed untyped eslint.ignoreDuringBuilds option. - outputFileTracingExcludes for ./.tmp-audio/** and bot-config.json to avoid Turbopack NFT pulling in the whole project. TypeScript / code quality: - audio/file/route.ts: Buffer → Uint8Array wrap for NextResponse BodyInit compatibility. - waveform-player.tsx: removed invalid 'volume' prop from <audio>, apply volume via ref. - auth.ts: NextAuthOptions type, module augmentation for session.user.id, dropped invalid 'trustHost' config key. - discord-bot.ts: clearer error messages for privileged-intent and invalid-token failures; process.exit(1) on login failure so process managers can detect; unhandled rejection / uncaught exception handlers; fixed a console.log template-string typo. - eslint.config.mjs: react-hooks/exhaustive-deps re-enabled as 'warn' — disabling it was the root cause of the polling leak. - .env.example: documented AUTH_TRUST_HOST for non-localhost deploys. - .gitignore: ignore npm/yarn/pnpm lockfiles (project uses bun). - new src/lib/rate-limit.ts: in-memory token-bucket limiter. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full engineering pass on the Valency Studio codebase. Fixes one real bug that was leaking intervals in production, hardens API-key handling, and migrates to Next 16 conventions. All changes typecheck cleanly and the dev server starts in ~400ms.
Critical bug fixes
<roblox-panel.tsx>polling interval leak — the pollinguseEffecthad[uploadMap, account, ...]as deps and a cleanup that only cleared the interval when!needsPolling, so on every state change a new interval was created while the previous one kept running (multiplied polling, duplicate toasts, memory growth). Rewrote to a singlesetIntervalwithuseReffor the live account/setters; cleanup always runs.<history-list.tsx>polling — same pattern: refs so the interval is created once and doesn't recreate on every state change.<roblox-api.ts>verifyApiKeywas a no-op — it called/assets/v1/operations/verify-${Date.now()}(which doesn't exist) and treated the resulting 404 as valid. Any key would pass. Now probes a real asset path and discriminates 401/403 vs 200/404.<audio-processor.ts>getWaveformreturnedMath.random()on ffmpeg failure, so corrupt files displayed as plausible waveforms. Now returns[]so the UI can show a clear no-data state.<audio-processor.ts>ensureDirsmoved off module top-level — was creating directories on everyimport, which breaks on read-only filesystems and trips Turbopack's file tracer.Security
/api/roblox/status— API key moved from URL query string to POST body /X-Roblox-Api-Keyheader. The key no longer leaks into reverse-proxy access logs, browser history, or referer headers./api/roblox/verifyand/api/roblox/upload— lightweight in-memory rate limiting (10/min and 20/min respectively) to prevent DoS and getting our server throttled by Roblox./api/roblox/upload— explicit 100 MB cap on the in-memory audio buffer with a 413 response.Next.js 16 migration
src/middleware.ts→src/proxy.ts— Next 16 deprecated the old convention; the dev server was emitting a warning. The semantics are identical.next.config.ts:reactStrictMode: trueto catch effect-related bugs earlier;outputFileTracingExcludesfor./.tmp-audio/**andbot-config.jsonso standalone output doesn't pull the whole repo; removed the untypedeslint.ignoreDuringBuildsoption.TypeScript / code quality
audio/file/route.ts: wrap Buffers asUint8ArrayforNextResponseBodyInittyping.waveform-player.tsx: remove invalidvolumeprop on<audio>, apply via ref.auth.ts:NextAuthOptionstype,Sessionmodule augmentation foruser.id, drop invalidtrustHostconfig key (now set viaAUTH_TRUST_HOSTenv, documented in.env.example).discord-bot.ts: clear error messages for privileged-intent failures (the Engineering pass: fix polling leak, security hardening, and migrate to Next 16 conventions #1 reason a fresh bot silently fails to start) and invalid tokens;process.exit(1)on login failure;unhandledRejection/uncaughtExceptionhandlers; fixed a console-log template-string typo.eslint.config.mjs: re-enabledreact-hooks/exhaustive-depsas a warning. Disabling it was the root cause of the polling leak — the original effect deps looked right, but the rule was off so the cleanup-vs-interval mismatch wasn't flagged.src/lib/rate-limit.ts— in-memory token-bucket limiter with self-sweeping storage..env.example: documentedAUTH_TRUST_HOST..gitignore: ignore npm/yarn/pnpm lockfiles (project uses bun).Verification
npx tsc --noEmit --skipLibCheck— clean, zero errors (was 6 pre-existing errors; this PR also fixed those inauth.ts,waveform-player.tsx,audio/file/route.ts, and the originalroblox-panel.tsxtypecheck quirks).npx next dev— starts in ~400ms,/returns 307 →/login,/loginreturns 200, no deprecation warnings.npx next build— only fails on Google Fonts fetch + Prisma binary download in the sandboxed CI; both work in normal environments. The Turbopack compile and standalone output work.Files changed
18 files changed, +538 / −193 lines.