Skip to content

Merge test into main - #551

Merged
sweetmantech merged 3 commits into
mainfrom
test
May 11, 2026
Merged

Merge test into main#551
sweetmantech merged 3 commits into
mainfrom
test

Conversation

@sweetmantech

@sweetmantechsweetmantech commented May 11, 2026

Copy link
Copy Markdown
Contributor

Batched promotion of test → main.

Included PRs

🤖 Generated with Claude Code


Summary by cubic

Add POST /api/sandboxes/staged-file for 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

    • New route with OPTIONS preflight using getCorsHeaders().
    • Handshake auth via validateAuthContext using Bearer Authorization for blob.generate-client-token; skips auth for blob.upload-completed.
    • Enforces 100MB max size and random filename suffix via @vercel/blob/client.handleUpload.
    • Generic 500 on unexpected errors with server-side logging; all responses include CORS headers.
  • Migration

    • Point client handleUploadUrl to /api/sandboxes/staged-file.
    • Send a Bearer Authorization header on the handshake; no header is required for the upload-completed callback.

Written for commit 329c589. Summary will update on new commits.

sweetmantechand others added 3 commits May 11, 2026 16:16
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>
@vercel

vercelBot commented May 11, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
apiReadyReadyPreviewMay 11, 2026 11:07pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 33 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8fdaa19-a476-43bb-815a-c7233fa6ee77

📥 Commits

Reviewing files that changed from the base of the PR and between 710f789 and 329c589.

⛔ Files ignored due to path filters (1)
  • lib/sandbox/__tests__/postSandboxesUploadTokensHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (2)
  • app/api/sandboxes/staged-file/route.ts
  • lib/sandbox/postSandboxesUploadTokensHandler.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-aicubic-dev-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 singular staged-file path appears inconsistent with the documented plural migration target (staged-files), which could break endpoint routing.
  • lib/sandbox/postSandboxesUploadTokensHandler.ts currently treats malformed JSON as a 500 path, so client-side bad input is misclassified as server failure instead of returning a 400 before handleUpload, 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.ts and lib/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
Loading

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> {

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: 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" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.)

View Feedback

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>
Suggested change
{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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>

@sweetmantech
sweetmantech merged commit d14d59f into mainMay 11, 2026
7 checks passed
Sign up for freeto 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.

2 participants

@sweetmantech@arpitgupta1214