feat(api): migrate POST /api/upload from chat (Arweave bytes proxy)#492
feat(api): migrate POST /api/upload from chat (Arweave bytes proxy)#492arpitgupta1214 wants to merge 2 commits intotestfrom
Conversation
Mirrors chat-side `/api/upload` shape so chat callers can migrate with a
base-URL swap. Multipart `file` field → Arweave upload → returns
`{ success, fileName, fileType, fileSize, url }` (200) or
`{ success: false, error }` (500). Reuses existing `uploadToArweave()`
and `getFetchableUrl()` libs.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR adds a file upload API endpoint integrated with Arweave storage. The implementation includes a POST handler that processes multipart form data, validates and uploads files to Arweave, a CORS-enabled OPTIONS preflight handler, error handling with logging, and route configuration for dynamic execution and no-cache behavior. ChangesArweave File Upload Endpoint
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes The changes introduce straightforward file upload handling with clear separation of concerns: a handler module containing business logic and a route module wiring HTTP methods. The code follows standard Next.js patterns without complex control flow or intricate dependencies. Two distinct files with minimal interdependency and no refactoring or conditional logic. Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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.
1 issue found across 3 files
Confidence score: 3/5
- There is a concrete security/privacy risk in
lib/arweave/uploadFileHandler.ts: 500 responses currently expose raw exception messages, which can leak internal implementation details to clients. - Because this is severity 7/10 with high confidence, the change carries some merge risk until the handler returns a generic error string for server failures.
- Pay close attention to
lib/arweave/uploadFileHandler.ts- replace raw error text in 500 responses with a sanitized generic message.
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/arweave/uploadFileHandler.ts">
<violation number="1" location="lib/arweave/uploadFileHandler.ts:50">
P1: Do not expose raw exception messages in 500 responses; return a generic error string instead.
(Based on your team's feedback about preventing internal error detail leaks in API 500 responses.) [FEEDBACK_USED]</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client
participant Route as API Route (app/api/upload)
participant Handler as uploadFileHandler
participant Utils as Arweave Utilities
participant Arweave as Arweave Network
Note over Client, Arweave: CORS Preflight
Client->>Route: OPTIONS /api/upload
Route-->>Client: 200 OK (CORS Headers)
Note over Client, Arweave: File Upload Flow
Client->>Route: POST /api/upload (multipart/form-data)
Route->>Handler: NEW: uploadFileHandler(request)
Handler->>Handler: Parse FormData (field: "file")
alt No file provided
Handler-->>Route: 500 Internal Server Error
Route-->>Client: { success: false, error: "No file provided" }
else File exists
Handler->>Handler: Convert to Buffer & Extract Metadata
Handler->>Utils: uploadToArweave(buffer, contentType)
Utils->>Arweave: Dispatch Transaction
Arweave-->>Utils: Transaction ID (tx_id)
Utils-->>Handler: { id: tx_id }
alt Upload Success
Handler->>Utils: getFetchableUrl(ar://tx_id)
Utils-->>Handler: https://arweave.net/tx_id
Handler-->>Route: 200 OK + JSON Payload
Route-->>Client: { success: true, fileName, fileType, fileSize, url }
else Upload Failure
Utils-->>Handler: Throw Error
Handler-->>Route: 500 Internal Server Error
Route-->>Client: { success: false, error: "message" }
end
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| return NextResponse.json( | ||
| { | ||
| success: false, | ||
| error: error instanceof Error ? error.message : "Unknown error", |
There was a problem hiding this comment.
P1: Do not expose raw exception messages in 500 responses; return a generic error string instead.
(Based on your team's feedback about preventing internal error detail leaks in API 500 responses.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/arweave/uploadFileHandler.ts, line 50:
<comment>Do not expose raw exception messages in 500 responses; return a generic error string instead.
(Based on your team's feedback about preventing internal error detail leaks in API 500 responses.) </comment>
<file context>
@@ -0,0 +1,58 @@
+ return NextResponse.json(
+ {
+ success: false,
+ error: error instanceof Error ? error.message : "Unknown error",
+ },
+ {
</file context>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
lib/arweave/uploadFileHandler.ts (2)
18-23: ⚡ Quick winUse a Zod-based validate function for upload input shape.
Manual checks work, but this route should use a validate function with Zod for consistent parsing/error behavior across API endpoints.
As per coding guidelines, "All API endpoints should use a validate function for input parsing using Zod for schema validation".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/arweave/uploadFileHandler.ts` around lines 18 - 23, Replace the manual file presence/type checks in uploadFileHandler with a Zod-based validate call: define a Zod schema (e.g., uploadInputSchema) that validates the expected upload shape (file presence, correct File-like type and any allowed mime/size rules), extract fields from request.formData() into a plain object, call validate(uploadInputSchema, parsedInput) and use the returned value (instead of manual checks) inside uploadFileHandler; remove the throw new Error("No file provided") branch and rely on validate to produce consistent validation errors across endpoints.
16-58: ⚡ Quick winSplit
uploadFileHandlerinto smaller focused helpers.This function is doing parsing, validation, upload orchestration, success mapping, and error mapping in one block and exceeds the 20-line guideline. Please extract at least file parsing/validation and response construction helpers.
As per coding guidelines, "Flag functions longer than 20 lines" and "Keep functions small and focused".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/arweave/uploadFileHandler.ts` around lines 16 - 58, Split uploadFileHandler into smaller helpers: extract request parsing/validation into a parseAndValidateFile helper that accepts the NextRequest, calls request.formData(), validates the "file" field, converts to Buffer and returns {fileBuffer, fileSize, fileType, fileName} (use the same Buffer/from(await file.arrayBuffer()) logic and the same type/name fallbacks); extract response mapping into a buildJsonResponse helper that wraps NextResponse.json with getCorsHeaders and accepts payload + status to produce the final response; then refactor uploadFileHandler to call parseAndValidateFile, call uploadToArweave(fileBuffer, fileType), map the transaction into the success payload using getFetchableUrl(`ar://${transaction.id}`) and file metadata, and return buildJsonResponse for both success and error paths so uploadFileHandler stays focused and under ~20 lines while still using getCorsHeaders, uploadToArweave, and getFetchableUrl.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/api/upload/route.ts`:
- Around line 27-29: The POST handler currently delegates straight to
uploadFileHandler without authentication; call await
validateAuthContext(request) at the start of the exported POST function
(importing it if necessary), handle failures by returning an appropriate 401/403
response, and only then call uploadFileHandler (optionally passing the returned
auth context) so uploads require valid x-api-key or Authorization: Bearer
credentials; keep the function signature (POST(request: NextRequest)) and
preserve uploadFileHandler usage.
In `@lib/arweave/uploadFileHandler.ts`:
- Around line 25-30: Before calling file.arrayBuffer() and Buffer.from(...) in
uploadFileHandler (which currently creates fileBuffer/fileSize and then calls
uploadToArweave), check the incoming file.size against a configured
MAX_FILE_SIZE constant and reject/throw an error (or return an HTTP 413)
immediately if file.size exceeds the limit; only if file.size is within bounds
proceed to await file.arrayBuffer(), create fileBuffer, set fileType/fileName,
and call uploadToArweave. Ensure the MAX_FILE_SIZE constant is documented and
used where the size check happens to prevent buffering oversized uploads.
---
Nitpick comments:
In `@lib/arweave/uploadFileHandler.ts`:
- Around line 18-23: Replace the manual file presence/type checks in
uploadFileHandler with a Zod-based validate call: define a Zod schema (e.g.,
uploadInputSchema) that validates the expected upload shape (file presence,
correct File-like type and any allowed mime/size rules), extract fields from
request.formData() into a plain object, call validate(uploadInputSchema,
parsedInput) and use the returned value (instead of manual checks) inside
uploadFileHandler; remove the throw new Error("No file provided") branch and
rely on validate to produce consistent validation errors across endpoints.
- Around line 16-58: Split uploadFileHandler into smaller helpers: extract
request parsing/validation into a parseAndValidateFile helper that accepts the
NextRequest, calls request.formData(), validates the "file" field, converts to
Buffer and returns {fileBuffer, fileSize, fileType, fileName} (use the same
Buffer/from(await file.arrayBuffer()) logic and the same type/name fallbacks);
extract response mapping into a buildJsonResponse helper that wraps
NextResponse.json with getCorsHeaders and accepts payload + status to produce
the final response; then refactor uploadFileHandler to call
parseAndValidateFile, call uploadToArweave(fileBuffer, fileType), map the
transaction into the success payload using
getFetchableUrl(`ar://${transaction.id}`) and file metadata, and return
buildJsonResponse for both success and error paths so uploadFileHandler stays
focused and under ~20 lines while still using getCorsHeaders, uploadToArweave,
and getFetchableUrl.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 42cfe620-47e1-49aa-b682-c3712b46c31c
⛔ Files ignored due to path filters (1)
lib/arweave/__tests__/uploadFileHandler.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (2)
app/api/upload/route.tslib/arweave/uploadFileHandler.ts
| export async function POST(request: NextRequest) { | ||
| return uploadFileHandler(request); | ||
| } |
There was a problem hiding this comment.
Add auth validation before delegating to the upload handler.
POST currently accepts uploads without calling validateAuthContext(). That leaves the endpoint open for unauthenticated writes.
Suggested fix
import { NextRequest, NextResponse } from "next/server";
import { getCorsHeaders } from "@/lib/networking/getCorsHeaders";
import { uploadFileHandler } from "@/lib/arweave/uploadFileHandler";
+import { validateAuthContext } from "@/lib/auth/validateAuthContext";
...
export async function POST(request: NextRequest) {
+ const auth = await validateAuthContext(request);
+ if (!auth.valid) {
+ return NextResponse.json(
+ { success: false, error: "Unauthorized" },
+ { status: 401, headers: getCorsHeaders() },
+ );
+ }
return uploadFileHandler(request);
}As per coding guidelines, "Always use validateAuthContext() for authentication in API routes; it supports both x-api-key header and Authorization: Bearer token authentication".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function POST(request: NextRequest) { | |
| return uploadFileHandler(request); | |
| } | |
| export async function POST(request: NextRequest) { | |
| const auth = await validateAuthContext(request); | |
| if (!auth.valid) { | |
| return NextResponse.json( | |
| { success: false, error: "Unauthorized" }, | |
| { status: 401, headers: getCorsHeaders() }, | |
| ); | |
| } | |
| return uploadFileHandler(request); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/upload/route.ts` around lines 27 - 29, The POST handler currently
delegates straight to uploadFileHandler without authentication; call await
validateAuthContext(request) at the start of the exported POST function
(importing it if necessary), handle failures by returning an appropriate 401/403
response, and only then call uploadFileHandler (optionally passing the returned
auth context) so uploads require valid x-api-key or Authorization: Bearer
credentials; keep the function signature (POST(request: NextRequest)) and
preserve uploadFileHandler usage.
| const fileBuffer = Buffer.from(await file.arrayBuffer()); | ||
| const fileSize = fileBuffer.length; | ||
| const fileType = file.type || "application/octet-stream"; | ||
| const fileName = file.name; | ||
|
|
||
| const transaction = await uploadToArweave(fileBuffer, fileType); |
There was a problem hiding this comment.
Enforce a maximum file size before buffering bytes.
arrayBuffer() + Buffer.from(...) loads the full payload into memory with no upper bound. Add a strict file.size cap and reject oversized uploads early to avoid memory exhaustion.
Suggested fix
+const MAX_UPLOAD_BYTES = 25 * 1024 * 1024; // 25MB
...
-const fileBuffer = Buffer.from(await file.arrayBuffer());
+if (file.size > MAX_UPLOAD_BYTES) {
+ throw new Error("File too large");
+}
+const fileBuffer = Buffer.from(await file.arrayBuffer());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const fileBuffer = Buffer.from(await file.arrayBuffer()); | |
| const fileSize = fileBuffer.length; | |
| const fileType = file.type || "application/octet-stream"; | |
| const fileName = file.name; | |
| const transaction = await uploadToArweave(fileBuffer, fileType); | |
| const MAX_UPLOAD_BYTES = 25 * 1024 * 1024; // 25MB | |
| // ... (other code) | |
| if (file.size > MAX_UPLOAD_BYTES) { | |
| throw new Error("File too large"); | |
| } | |
| const fileBuffer = Buffer.from(await file.arrayBuffer()); | |
| const fileSize = fileBuffer.length; | |
| const fileType = file.type || "application/octet-stream"; | |
| const fileName = file.name; | |
| const transaction = await uploadToArweave(fileBuffer, fileType); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/arweave/uploadFileHandler.ts` around lines 25 - 30, Before calling
file.arrayBuffer() and Buffer.from(...) in uploadFileHandler (which currently
creates fileBuffer/fileSize and then calls uploadToArweave), check the incoming
file.size against a configured MAX_FILE_SIZE constant and reject/throw an error
(or return an HTTP 413) immediately if file.size exceeds the limit; only if
file.size is within bounds proceed to await file.arrayBuffer(), create
fileBuffer, set fileType/fileName, and call uploadToArweave. Ensure the
MAX_FILE_SIZE constant is documented and used where the size check happens to
prevent buffering oversized uploads.
Adds dedicated
POST /api/uploadparity for chat's/api/upload(group 2 of the chat→api migration). Multipartfile→ Arweave upload → returns{ success, fileName, fileType, fileSize, url }(200) or{ success: false, error }(500). Reuses existinguploadToArweave()andgetFetchableUrl()libs.Test plan
curl -F file=@/tmp/img.png https://<preview>/api/uploadreturns 200 + Arweave gateway URLcurl -X POST https://<preview>/api/upload(no file) returns 500 +{ success: false, error: "No file provided" }lib/arweave/__tests__/uploadFileHandler.test.tsgreen; full suite still 2308/2308Summary by cubic
Adds
POST /api/uploadto proxy multipart file uploads to Arweave and return a gateway URL, matching chat’s/api/uploadfor a drop-in base-URL swap. Includes CORS preflight and CORS headers on all responses, with caching disabled for this route.New Features
POST /api/uploadacceptsmultipart/form-datawithfile, uploads bytes to Arweave, and responds with{ success, fileName, fileType, fileSize, url }(200) or{ success: false, error }(500).OPTIONShandler adds CORS headers viagetCorsHeaders(); success and error responses also include CORS headers.dynamic = "force-dynamic",fetchCache = "force-no-store",revalidate = 0.Migration
/api/upload; switch clients by changing the base URL only.Written for commit 812717f. Summary will update on new commits.
Summary by CodeRabbit