Merge test into main#551
Conversation
Brand-new accounts used to receive exactly 25 credits regardless of plan, because insertCreditsUsage had a hard-coded DEFAULT_CREDITS=25 fallback that both call sites (agent signup + account create) relied on. The new credits-balance endpoint exposes this as "25 / 333 used 308" the moment an account is provisioned. Fix at the API layer, reusing what PR #547 already gave us: - New `lib/credits/getAccountSubscriptionState.ts` — single source of truth for "is this account pro?". Extracts the parallel getActiveSubscriptionDetails + getOrgSubscription lookup that checkAndResetCredits already did inline. - `checkAndResetCredits` now delegates to that helper. Behavior unchanged; 7 lines collapse to 2. - New `lib/credits/initializeAccountCredits.ts` — plan-aware seeder. Looks up the subscription state via the new helper, then calls insertCreditsUsage with PRO_CREDITS=1000 or DEFAULT_CREDITS=333 (the constants we already exported in PR #547). - Both call sites swap insertCreditsUsage(id) for initializeAccountCredits(id): - lib/agents/createAccountWithEmail.ts - lib/accounts/createAccountHandler.ts - Remove the booby-trap default from insertCreditsUsage. The remainingCredits parameter is now required, so any new caller that forgets to pick a plan-aware value gets a type error. TDD: 4 new tests for getAccountSubscriptionState, 3 for initializeAccountCredits, full checkAndResetCredits suite migrated to mock the new helper instead of three Stripe functions. 234 tests green across 39 files. lint clean. No typecheck regressions in changed files (pre-existing AI-SDK type drift in getCreditUsage.test and handleChatCredits.test is unchanged). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…files (#541) * feat(api): add sandbox staged-files token handshake endpoint Adds POST /api/sandboxes/staged-files — the Vercel Blob client-upload handshake half of the sandbox upload flow. Mirrors chat's existing /api/sandbox/upload handler so the chat side can flip its handleUploadUrl to api and delete the local route. Auth follows the minimum-port shape: validates clientPayload.token because @vercel/blob/client.upload() does not allow setting an Authorization header on the handshake POST. The downstream commit (POST /api/sandboxes/files) re-authenticates with a real Bearer token. OPTIONS preflight is wired with getCorsHeaders() so cross-origin calls from chat.recoupable.com work the same way the existing /api/sandboxes/files route does. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): rename /api/sandboxes/staged-files to /api/sandboxes/stage-files Verb form matches /api/sandboxes/files (commit) and /api/sandboxes/file (get). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): rename /api/sandboxes/stage-files to /api/sandboxes/staged-file Noun-shaped resource matching /api/sandboxes/file (singular getter) and /api/sandboxes/files (collection commit). Each call generates a token to stage one file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): authenticate staged-file via Authorization header Drops clientPayload.token in favor of validateAuthContext() so the endpoint matches the auth surface of every other api route. The chat-side upload() now forwards an Authorization Bearer header via the library's headers option (which I missed earlier — that field is documented for exactly this use case). Branches on body.type to skip auth on the upload-completed callback; that POST comes from Vercel Blob's backend without the user's auth header, and handleUpload() verifies its signature internally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): hide raw exception details on staged-file 500s Log unexpected handleUpload errors server-side via console.error and return a generic 500 — matches the createSandboxHandler pattern and avoids leaking internal context in error responses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): trim JSDoc on staged-file handler and route Drop the multi-paragraph commentary; keep a one-liner on the handler explaining the asymmetric auth, and minimal JSDoc on the route per project convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): satisfy jsdoc/require-returns + require-param on staged-file route Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
4 issues found across 3 files
Confidence score: 2/5
- There is a high user-impact risk in
app/api/sandboxes/staged-file/route.ts: the singularstaged-filepath appears inconsistent with the documented plural migration target (staged-files), which could break endpoint routing. lib/sandbox/postSandboxesUploadTokensHandler.tscurrently treats malformed JSON as a 500 path, so client-side bad input is misclassified as server failure instead of returning a 400 beforehandleUpload, which can cause incorrect API behavior.- The remaining issues are lower severity (standardized 500 message and test-file length), but combined with the routing and request-validation concerns, this is not yet a low-risk merge.
- Pay close attention to
app/api/sandboxes/staged-file/route.tsandlib/sandbox/postSandboxesUploadTokensHandler.ts- route naming and JSON error handling are the main functional risks.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/sandbox/postSandboxesUploadTokensHandler.ts">
<violation number="1" location="lib/sandbox/postSandboxesUploadTokensHandler.ts:14">
P2: Invalid/malformed request bodies are handled as 500s because JSON parsing isn’t validated separately. Return a 400 for bad JSON before calling `handleUpload`.</violation>
<violation number="2" location="lib/sandbox/postSandboxesUploadTokensHandler.ts:37">
P2: Use the standardized 500 error message `"Internal server error"` in API responses.
(Based on your team's feedback about standardizing 500 responses to avoid leaking internal details.) [FEEDBACK_USED]</violation>
</file>
<file name="app/api/sandboxes/staged-file/route.ts">
<violation number="1" location="app/api/sandboxes/staged-file/route.ts:21">
P1: Route path appears to be singular (`staged-file`) while the migration target is documented as plural (`staged-files`), which can break the intended endpoint.</violation>
</file>
<file name="lib/sandbox/__tests__/postSandboxesUploadTokensHandler.test.ts">
<violation number="1" location="lib/sandbox/__tests__/postSandboxesUploadTokensHandler.test.ts:1">
P3: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
This new test file exceeds the repository’s 100-line maintainability limit.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client as Client Browser
participant Next as Next.js API Route
participant Handler as postSandboxesUploadTokensHandler
participant Auth as validateAuthContext
participant Blob as @vercel/blob/client (handleUpload)
participant BlobSvc as Vercel Blob Service
Note over Client,BlobSvc: NEW: Staged-file upload flow
Client->>Next: OPTIONS /api/sandboxes/staged-file
Next-->>Client: 200 CORS preflight (getCorsHeaders)
alt Handshake (blob.generate-client-token)
Client->>Next: POST /api/sandboxes/staged-file<br/>{type:"blob.generate-client-token", ...}<br/>Authorization: Bearer <token>
Next->>Handler: postSandboxesUploadTokensHandler(request)
Handler->>Handler: Parse body as HandleUploadBody
Handler->>Auth: validateAuthContext(request)
alt Auth succeeds
Auth-->>Handler: { accountId, orgId, authToken }
Handler->>Blob: handleUpload({ body, request,<br/>onBeforeGenerateToken,<br/>onUploadCompleted })
Blob->>BlobSvc: Generate client upload token
Note over Blob: onBeforeGenerateToken returns<br/>constraints: 100MB max, random suffix
BlobSvc-->>Blob: clientToken
Blob-->>Handler: {type:"blob.generate-client-token", clientToken}
Handler->>Handler: NextResponse.json + getCorsHeaders()
Handler-->>Next: 200 JSON with clientToken
Next-->>Client: 200 + CORS headers
else Auth fails
Auth-->>Handler: NextResponse (401)
Handler-->>Next: 401 Unauthorized
Next-->>Client: 401 + CORS headers
end
else Upload completed callback (blob.upload-completed)
Client->>Next: POST /api/sandboxes/staged-file<br/>{type:"blob.upload-completed", ...}
Next->>Handler: postSandboxesUploadTokensHandler(request)
Handler->>Handler: body.type !== "blob.generate-client-token"
Note over Handler: Auth SKIPPED — callback is signature-verified<br/>by handleUpload() internally
Handler->>Blob: handleUpload({ body, request,<br/>onBeforeGenerateToken,<br/>onUploadCompleted })
Blob-->>Handler: {type:"blob.upload-completed"}
Handler->>Handler: NextResponse.json + getCorsHeaders()
Handler-->>Next: 200 + CORS headers
Next-->>Client: 200 + CORS headers
end
Note over Handler,BlobSvc: ERROR HANDLING
alt handleUpload throws
Handler->>Handler: console.error(...)
Handler->>Handler: NextResponse.json({status:"error",<br/>error:"Failed to issue upload token"}, 500)
Handler-->>Next: 500 + CORS headers
Next-->>Client: 500 + CORS headers
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| * @param request - The request object. | ||
| * @returns A NextResponse with the handshake result or error. | ||
| */ | ||
| export async function POST(request: NextRequest): Promise<Response> { |
There was a problem hiding this comment.
P1: Route path appears to be singular (staged-file) while the migration target is documented as plural (staged-files), which can break the intended endpoint.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/sandboxes/staged-file/route.ts, line 21:
<comment>Route path appears to be singular (`staged-file`) while the migration target is documented as plural (`staged-files`), which can break the intended endpoint.</comment>
<file context>
@@ -0,0 +1,23 @@
+ * @param request - The request object.
+ * @returns A NextResponse with the handshake result or error.
+ */
+export async function POST(request: NextRequest): Promise<Response> {
+ return postSandboxesUploadTokensHandler(request);
+}
</file context>
| } catch (error) { | ||
| console.error("[postSandboxesUploadTokensHandler] handleUpload failed:", error); | ||
| return NextResponse.json( | ||
| { status: "error", error: "Failed to issue upload token" }, |
There was a problem hiding this comment.
P2: Use the standardized 500 error message "Internal server error" in API responses.
(Based on your team's feedback about standardizing 500 responses to avoid leaking internal details.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/sandbox/postSandboxesUploadTokensHandler.ts, line 37:
<comment>Use the standardized 500 error message `"Internal server error"` in API responses.
(Based on your team's feedback about standardizing 500 responses to avoid leaking internal details.) </comment>
<file context>
@@ -0,0 +1,41 @@
+ } catch (error) {
+ console.error("[postSandboxesUploadTokensHandler] handleUpload failed:", error);
+ return NextResponse.json(
+ { status: "error", error: "Failed to issue upload token" },
+ { status: 500, headers: getCorsHeaders() },
+ );
</file context>
| { status: "error", error: "Failed to issue upload token" }, | |
| { status: "error", error: "Internal server error" }, |
| request: NextRequest, | ||
| ): Promise<NextResponse> { | ||
| try { | ||
| const body = (await request.json()) as HandleUploadBody; |
There was a problem hiding this comment.
P2: Invalid/malformed request bodies are handled as 500s because JSON parsing isn’t validated separately. Return a 400 for bad JSON before calling handleUpload.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/sandbox/postSandboxesUploadTokensHandler.ts, line 14:
<comment>Invalid/malformed request bodies are handled as 500s because JSON parsing isn’t validated separately. Return a 400 for bad JSON before calling `handleUpload`.</comment>
<file context>
@@ -0,0 +1,41 @@
+ request: NextRequest,
+): Promise<NextResponse> {
+ try {
+ const body = (await request.json()) as HandleUploadBody;
+
+ if (body.type === "blob.generate-client-token") {
</file context>
| @@ -0,0 +1,124 @@ | |||
| import { describe, it, expect, vi, beforeEach } from "vitest"; | |||
There was a problem hiding this comment.
P3: Custom agent: Enforce Clear Code Style and Maintainability Practices
This new test file exceeds the repository’s 100-line maintainability limit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/sandbox/__tests__/postSandboxesUploadTokensHandler.test.ts, line 1:
<comment>This new test file exceeds the repository’s 100-line maintainability limit.</comment>
<file context>
@@ -0,0 +1,124 @@
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { NextResponse } from "next/server";
+import type { NextRequest } from "next/server";
</file context>
Batched promotion of test → main.
Included PRs
🤖 Generated with Claude Code
Summary by cubic
Add
POST /api/sandboxes/staged-filefor Vercel Blob client-upload token handshakes and upload-completed callbacks, replacing/api/sandbox/upload. Includes CORS preflight and auth aligned with other API routes.New Features
OPTIONSpreflight usinggetCorsHeaders().validateAuthContextusing BearerAuthorizationforblob.generate-client-token; skips auth forblob.upload-completed.@vercel/blob/client.handleUpload.Migration
handleUploadUrlto/api/sandboxes/staged-file.Authorizationheader on the handshake; no header is required for the upload-completed callback.Written for commit 329c589. Summary will update on new commits.