feat: Implement College Marketplace with Authentication, Media Uploads, and Seller Controls#239
feat: Implement College Marketplace with Authentication, Media Uploads, and Seller Controls#239Rohank3 wants to merge 3 commits intoiiitl:mainfrom
Conversation
|
@Rohank3 is attempting to deploy a commit to the mrimmortal09's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a marketplace feature: new Product Mongoose model, five product API route handlers (list/create/update/delete/mark sold/mark available) plus comments, Cloudinary image handling, and middleware/config updates to expose /api/products; several non-functional formatting/whitespace edits elsewhere. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Server as Next.js API
participant Auth as verifyJwt
participant Cloudinary
participant DB as MongoDB
Client->>Server: POST /api/products (multipart form + image)
Server->>Auth: verifyJwt(token)
Auth-->>Server: userId
Server->>Server: validate fields, image type/size
Server->>Cloudinary: uploadOnCloudinary(tempFile)
Cloudinary-->>Server: image_url
Server->>DB: Product.create({...seller: userId, image_url})
DB-->>Server: created product
Server-->>Client: 201 { message, product }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 1
🧹 Nitpick comments (9)
app/api/products/[id]/route.ts (2)
104-113: Consider adding maxlength validation for contact_info.Similar to description,
contact_infohas a 200-character schema limit that isn't validated at the route level.♻️ Proposed fix
if (contact_info !== undefined) { const trimmed = String(contact_info).trim() if (!trimmed) { return NextResponse.json( { message: 'Contact info cannot be empty' }, { status: 400 } ) } + if (trimmed.length > 200) { + return NextResponse.json( + { message: 'Contact info cannot exceed 200 characters' }, + { status: 400 } + ) + } updateData.contact_info = trimmed }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/`[id]/route.ts around lines 104 - 113, The route handles contact_info but doesn't enforce the 200-character schema limit; update the validation in the contact_info block (where contact_info is trimmed and assigned to updateData.contact_info) to check trimmed.length <= 200 and return a 400 NextResponse.json with a clear message (e.g., "Contact info must be 200 characters or fewer") when the length exceeds 200, mirroring the description maxlength validation behavior.
82-91: Consider adding maxlength validation for description.The
titlefield validates max 40 characters at the route level (line 73), butdescriptiondoesn't validate its 1000-character limit. While the schema validator will catch this (viarunValidators: true), consistent early validation provides clearer error messages.♻️ Proposed fix
if (description !== undefined) { const trimmed = String(description).trim() if (!trimmed) { return NextResponse.json( { message: 'Description cannot be empty' }, { status: 400 } ) } + if (trimmed.length > 1000) { + return NextResponse.json( + { message: 'Description cannot exceed 1000 characters' }, + { status: 400 } + ) + } updateData.description = trimmed }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/`[id]/route.ts around lines 82 - 91, Add a max-length check for the incoming description in the request handler so it mirrors the title validation: when description !== undefined, after trimming (the existing trimmed variable) verify trimmed.length <= 1000 and return NextResponse.json({ message: 'Description cannot exceed 1000 characters' }, { status: 400 }) if it’s too long; otherwise assign updateData.description = trimmed. Target the block handling description in route.ts (the trimmed variable / updateData.description) to implement this early validation.app/api/products/[id]/comments/route.ts (2)
4-6: Remove unused imports.
UserandICommentare imported but never used. Static analysis confirms this.♻️ Proposed fix
-import Product, { IComment } from '@/model/Product' -import User from '@/model/User' +import Product from '@/model/Product'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/`[id]/comments/route.ts around lines 4 - 6, Remove the unused imports to clean up the module: delete the separate import of User (import User from '@/model/User') and remove IComment from the named imports on the Product import (import Product, { IComment } from '@/model/Product'); leave only the symbols actually used (e.g., Product) and keep other existing imports like verifyJwt intact, or convert any needed type-only imports to a "type" import if later required.
73-76: Replaceanywith proper typing.The static analysis flagged multiple
anyusages. Use Mongoose's subdocument types.♻️ Proposed fix
+import { Types } from 'mongoose' + const parentComment = product.comments.find( - (c: any) => c._id && c._id.toString() === parentCommentId -) as any + (c) => c._id && c._id.toString() === parentCommentId +) if (!parentComment) {For line 96, Mongoose subdocuments accept the object shape without explicit casting when the schema matches.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/`[id]/comments/route.ts around lines 73 - 76, The find call uses broad any types for product.comments and parentComment which defeats type safety; replace these anys by importing and using the Mongoose/Schema types for your product and comment subdocument (e.g., ProductDocument / CommentSubdocument or your defined Comment interface), type product.comments as Comment[] (or Types.DocumentArray<CommentSubdocument>), remove the "as any" cast, and declare parentComment with the correct type (e.g., CommentSubdocument | undefined); update the predicate to use optional chaining (c._id?.toString() === parentCommentId) so the compiler can infer types correctly.app/api/products/[id]/sold/route.ts (1)
76-93: Potential race condition on quantity decrement.The read-check-modify-save pattern between lines 76-93 is not atomic. Two concurrent requests could both pass the
sold_quantity > product.quantitycheck and oversell.For a college marketplace with low traffic, this risk is minimal, but consider using an atomic update if inventory accuracy is important.
♻️ Atomic update approach
// Use findOneAndUpdate with conditions for atomicity const result = await Product.findOneAndUpdate( { _id: id, seller: userId, quantity: { $gte: sold_quantity } }, { $inc: { quantity: -sold_quantity }, }, { new: true } ) if (!result) { // Either not found, not owner, or insufficient quantity return NextResponse.json( { message: 'Cannot complete sale - product unavailable or insufficient quantity' }, { status: 400 } ) } // Then handle is_sold flag update if quantity reached 0 if (result.quantity <= 0) { result.is_sold = true result.show_when_sold = show_when_sold result.quantity = 0 await result.save() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/`[id]/sold/route.ts around lines 76 - 93, The current read-check-modify-save flow around product.quantity and product.save() can race and oversell; replace it with an atomic update using Product.findOneAndUpdate where the query includes {_id: id, seller: userId, quantity: { $gte: sold_quantity }} and the update uses $inc: { quantity: -sold_quantity } to ensure the decrement only occurs if enough stock exists, then check if the returned document is null (meaning not found/insufficient qty) and return the 400 response, and if result.quantity <= 0 set result.is_sold and result.show_when_sold and result.quantity = 0 then persist (result.save()).model/Product.ts (2)
92-99: Consider adding a compound index for visibility queries.The GET endpoint filters products using
{ is_sold, show_when_sold }. A compound index would improve query performance as the collection grows.📊 Suggested index
is_sold: { type: Boolean, default: false, + index: true, }, show_when_sold: { type: Boolean, default: false, },Alternatively, add a compound index at the schema level:
ProductSchema.index({ is_sold: 1, show_when_sold: 1 })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@model/Product.ts` around lines 92 - 99, The schema lacks a compound index for the visibility fields used in queries; add a compound index on is_sold and show_when_sold by calling ProductSchema.index({ is_sold: 1, show_when_sold: 1 }) after the ProductSchema definition (and before exporting the model) so Mongo/Mongoose will create the index and speed up queries that filter by these two fields.
65-68: Use proper typing instead ofanyin validator.The static analysis flagged
anyusage. Since this validatesbulk_discounts, use the correct type.♻️ Proposed fix
validate: [ - (val: any[]) => val.length <= 10, + (val: { min_quantity: number; discount_per_item: number }[]) => val.length <= 10, 'Exceeds the limit of 10 bulk discount conditions', ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@model/Product.ts` around lines 65 - 68, The validator for the bulk_discounts field uses `any[]`; replace that with the concrete type for bulk discounts (e.g., `BulkDiscount[]` or `Array<BulkDiscount>`) and import or declare the `BulkDiscount` interface/type if missing, then change the validator signature from `(val: any[]) => ...` to `(val: BulkDiscount[]) => val.length <= 10`; update the validate array on the `bulk_discounts` field in the Product model so static analysis no longer sees `any`.app/api/products/route.ts (2)
7-7: Remove unused import.
Useris imported but never used in this file.♻️ Proposed fix
-import User from '@/model/User'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/route.ts` at line 7, Remove the unused import symbol User from the top-level imports in this module (the import line "import User from '@/model/User'"); update the import list to only include actually used symbols and run the linter/TS compiler to ensure no other references to User remain before committing.
14-18: Remove dead code:export const config = { api: { bodyParser: false } }has no effect in App Router.The
bodyParserconfiguration is Pages Router syntax and does not apply to route handlers in the App Router. The route handler correctly usesawait req.formData()for form parsing, making this export redundant and misleading.♻️ Proposed fix
-export const config = { - api: { - bodyParser: false, - }, -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/route.ts` around lines 14 - 18, Remove the dead Pages Router config export by deleting the export const config = { api: { bodyParser: false } } statement; in App Router route handlers the bodyParser option is ignored and this file already uses await req.formData(), so remove the redundant export (look for the symbol export const config and the bodyParser key) to avoid misleading configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/products/route.ts`:
- Around line 46-55: The search string is interpolated directly into MongoDB
$regex (inside the filter.$and -> $or clauses for title and description), which
risks special-character injection and unexpected regex behavior; before using
the search variable, escape regex metacharacters (e.g., replace all instances of
. * + ? ^ $ { } ( ) | [ ] \ with an escaped version) and use the escaped value
when constructing the $regex for title and description so the query matches the
literal search term safely and predictably.
---
Nitpick comments:
In `@app/api/products/`[id]/comments/route.ts:
- Around line 4-6: Remove the unused imports to clean up the module: delete the
separate import of User (import User from '@/model/User') and remove IComment
from the named imports on the Product import (import Product, { IComment } from
'@/model/Product'); leave only the symbols actually used (e.g., Product) and
keep other existing imports like verifyJwt intact, or convert any needed
type-only imports to a "type" import if later required.
- Around line 73-76: The find call uses broad any types for product.comments and
parentComment which defeats type safety; replace these anys by importing and
using the Mongoose/Schema types for your product and comment subdocument (e.g.,
ProductDocument / CommentSubdocument or your defined Comment interface), type
product.comments as Comment[] (or Types.DocumentArray<CommentSubdocument>),
remove the "as any" cast, and declare parentComment with the correct type (e.g.,
CommentSubdocument | undefined); update the predicate to use optional chaining
(c._id?.toString() === parentCommentId) so the compiler can infer types
correctly.
In `@app/api/products/`[id]/route.ts:
- Around line 104-113: The route handles contact_info but doesn't enforce the
200-character schema limit; update the validation in the contact_info block
(where contact_info is trimmed and assigned to updateData.contact_info) to check
trimmed.length <= 200 and return a 400 NextResponse.json with a clear message
(e.g., "Contact info must be 200 characters or fewer") when the length exceeds
200, mirroring the description maxlength validation behavior.
- Around line 82-91: Add a max-length check for the incoming description in the
request handler so it mirrors the title validation: when description !==
undefined, after trimming (the existing trimmed variable) verify trimmed.length
<= 1000 and return NextResponse.json({ message: 'Description cannot exceed 1000
characters' }, { status: 400 }) if it’s too long; otherwise assign
updateData.description = trimmed. Target the block handling description in
route.ts (the trimmed variable / updateData.description) to implement this early
validation.
In `@app/api/products/`[id]/sold/route.ts:
- Around line 76-93: The current read-check-modify-save flow around
product.quantity and product.save() can race and oversell; replace it with an
atomic update using Product.findOneAndUpdate where the query includes {_id: id,
seller: userId, quantity: { $gte: sold_quantity }} and the update uses $inc: {
quantity: -sold_quantity } to ensure the decrement only occurs if enough stock
exists, then check if the returned document is null (meaning not
found/insufficient qty) and return the 400 response, and if result.quantity <= 0
set result.is_sold and result.show_when_sold and result.quantity = 0 then
persist (result.save()).
In `@app/api/products/route.ts`:
- Line 7: Remove the unused import symbol User from the top-level imports in
this module (the import line "import User from '@/model/User'"); update the
import list to only include actually used symbols and run the linter/TS compiler
to ensure no other references to User remain before committing.
- Around line 14-18: Remove the dead Pages Router config export by deleting the
export const config = { api: { bodyParser: false } } statement; in App Router
route handlers the bodyParser option is ignored and this file already uses await
req.formData(), so remove the redundant export (look for the symbol export const
config and the bodyParser key) to avoid misleading configuration.
In `@model/Product.ts`:
- Around line 92-99: The schema lacks a compound index for the visibility fields
used in queries; add a compound index on is_sold and show_when_sold by calling
ProductSchema.index({ is_sold: 1, show_when_sold: 1 }) after the ProductSchema
definition (and before exporting the model) so Mongo/Mongoose will create the
index and speed up queries that filter by these two fields.
- Around line 65-68: The validator for the bulk_discounts field uses `any[]`;
replace that with the concrete type for bulk discounts (e.g., `BulkDiscount[]`
or `Array<BulkDiscount>`) and import or declare the `BulkDiscount`
interface/type if missing, then change the validator signature from `(val:
any[]) => ...` to `(val: BulkDiscount[]) => val.length <= 10`; update the
validate array on the `bulk_discounts` field in the Product model so static
analysis no longer sees `any`.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 82951500-53a6-447b-ad55-51d7b53d2922
📒 Files selected for processing (20)
app/api/chat/messages/route.tsapp/api/products/[id]/available/route.tsapp/api/products/[id]/comments/route.tsapp/api/products/[id]/route.tsapp/api/products/[id]/sold/route.tsapp/api/products/route.tsapp/layout.jsxapp/marketplace/page.jsxapp/quick-reads/page.tsxapp/upload-notes/page.tsxapp/upload-papers/page.tsxcomponents/chat/ChatWidget.tsxcomponents/theme-toggler.tsxhooks/useChatMessages.tshooks/useSemesterAutofill.tslib/eventEmitter.tsmiddleware.tsmodel/Message.tsmodel/Product.tsnext.config.ts
💤 Files with no reviewable changes (5)
- lib/eventEmitter.ts
- components/chat/ChatWidget.tsx
- model/Message.ts
- hooks/useChatMessages.ts
- app/api/chat/messages/route.ts
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
app/api/products/route.ts (2)
242-244: Temp file cleanup is duplicated after Cloudinary helper cleanup.At Line [242]-Line [244],
fs.unlinkrepeats cleanup already handled inhelpers/cloudinary.ts(finallyblock). Consider removing this duplicate path for clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/route.ts` around lines 242 - 244, The final cleanup in route.ts duplicates temp file deletion already done in the Cloudinary helper; remove the redundant await fs.unlink(tempFilePath).catch(() => {}) from the finally block in app/api/products/route.ts so the tempFilePath is only unlinked by the Cloudinary helper (helpers/cloudinary.ts), or alternatively move responsibility entirely into the route's finally and delete the unlink in helpers/cloudinary.ts — update the code so only one of the two (route.ts's finally or helpers/cloudinary.ts cleanup) performs fs.unlink on tempFilePath.
33-33: WhitelistsortByfields before passing into Mongo sort.Line [33] and Line [69] accept arbitrary field names from query params. Restricting this to known sortable fields avoids inefficient or unintended sorts.
Suggested fix
-const sortBy = searchParams.get('sortBy') || 'created_at' +const allowedSortFields = new Set(['created_at', 'price', 'title', 'quantity']) +const requestedSortBy = searchParams.get('sortBy') || 'created_at' +const sortBy = allowedSortFields.has(requestedSortBy) + ? requestedSortBy + : 'created_at'Also applies to: 69-69
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/products/route.ts` at line 33, Query param values from searchParams.get('sortBy') (stored in sortBy) are used directly in Mongo sort; whitelist acceptable sortable fields first and map incoming values to those safe field names before building the sort object (same check where sortBy is read and later where the sort object is constructed around line 69). Implement a const allowedSortFields = new Set([...]) or mapping of external names to internal DB keys, validate sortBy against it and fall back to 'created_at' (or return a 400) if invalid, then use the validated/mapped field when creating the Mongo sort to prevent arbitrary field injection and inefficient sorts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/products/route.ts`:
- Line 7: The file imports an unused symbol; remove the unused import statement
"import User from '@/model/User'" from app/api/products/route.ts to satisfy
linting and eliminate dead code—search for that exact import line and delete it
so no reference to User remains in the module.
- Around line 30-31: Validate and clamp parsed pagination params to avoid
NaN/zero/negative values: when reading page and limit (where parseInt is
currently used to set variables page and limit), coerce NaN to defaults and
clamp page to a minimum of 1 and limit to the range 1..50; then use these
sanitized values for computing skip (used around the skip calculation) and
totalPages (used where totalPages is computed) so downstream math is safe and
cannot produce 0/negative/NaN results.
- Around line 40-44: The current branch in route.ts sets filter.seller when
seller is present but omits the visibility constraint, allowing hidden sold
listings through; always apply a visibility clause (e.g., an $or with {is_sold:
false} and {is_sold: true, show_when_sold: true}) regardless of whether seller
is provided. Update the logic around the filter variable (the seller check in
route.ts) to compose the filter by merging seller into the filter and also
adding the visibilityClause (referencing seller, filter, is_sold, and
show_when_sold) so the public endpoint always enforces unsold or
sold-and-visible constraints.
- Around line 14-18: Remove the dead export named "config" (export const config
= { api: { bodyParser: false } }) from the App Router route handler because App
Router ignores that Pages Router shape; instead, update the route handler(s)
(e.g., your GET/POST handler functions) to explicitly parse the request body
with request.json(), request.text(), or request.formData() as appropriate and
adjust any downstream code that assumed bodyParser:false.
---
Nitpick comments:
In `@app/api/products/route.ts`:
- Around line 242-244: The final cleanup in route.ts duplicates temp file
deletion already done in the Cloudinary helper; remove the redundant await
fs.unlink(tempFilePath).catch(() => {}) from the finally block in
app/api/products/route.ts so the tempFilePath is only unlinked by the Cloudinary
helper (helpers/cloudinary.ts), or alternatively move responsibility entirely
into the route's finally and delete the unlink in helpers/cloudinary.ts — update
the code so only one of the two (route.ts's finally or helpers/cloudinary.ts
cleanup) performs fs.unlink on tempFilePath.
- Line 33: Query param values from searchParams.get('sortBy') (stored in sortBy)
are used directly in Mongo sort; whitelist acceptable sortable fields first and
map incoming values to those safe field names before building the sort object
(same check where sortBy is read and later where the sort object is constructed
around line 69). Implement a const allowedSortFields = new Set([...]) or mapping
of external names to internal DB keys, validate sortBy against it and fall back
to 'created_at' (or return a 400) if invalid, then use the validated/mapped
field when creating the Mongo sort to prevent arbitrary field injection and
inefficient sorts.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8a289f7e-c2e8-4cae-8369-44f530e20261
📒 Files selected for processing (1)
app/api/products/route.ts
| import path from 'path' | ||
| import { uploadOnCloudinary } from '@/helpers/cloudinary' | ||
| import Product from '@/model/Product' | ||
| import User from '@/model/User' |
There was a problem hiding this comment.
Remove unused import to satisfy lint and reduce dead code.
Line [7] imports User but it is not used in this module.
🧰 Tools
🪛 GitHub Check: Lint
[warning] 7-7:
'User' is defined but never used
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/products/route.ts` at line 7, The file imports an unused symbol;
remove the unused import statement "import User from '@/model/User'" from
app/api/products/route.ts to satisfy linting and eliminate dead code—search for
that exact import line and delete it so no reference to User remains in the
module.
| export const config = { | ||
| api: { | ||
| bodyParser: false, | ||
| }, | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Next.js App Router (app/**/route.ts), is export const config = { api: { bodyParser: false } }supported, or is it only forpages/api routes?
💡 Result:
No, export const config = { api: { bodyParser: false } } is not supported in Next.js App Router route handlers (app//route.ts). It is only supported in Pages Router API routes (pages/api). In App Router route handlers, there is no automatic body parsing like in Pages API routes. Instead, use Web Standard Request APIs such as await request.json, await request.text, or await request.formData to read the body manually as needed. Route handlers do not use a config export for body parsing; configuration for routes uses different exports like dynamic for caching behavior. Official Next.js documentation and discussions confirm that the config.api.bodyParser is specific to Pages Router API routes. Attempts to use it in App Router do not work, as shown in community examples where users tried and failed, confirming the need to use Request methods instead.
Citations:
- 1: Get Body if bodyParser is Disabled vercel/next.js#12517
- 2: https://rc.nextjs.org/docs/pages/building-your-application/routing/api-routes
- 3: https://stackoverflow.com/questions/76076544/bodyparser-does-not-work-on-nextjs-13-3-0
- 4: https://stackoverflow.com/questions/75652354/how-to-access-request-body-in-nextjs-13-2-route-handler
Remove config export—it's ignored in App Router route handlers.
This config shape is from Pages Router (pages/api) and is not supported in App Router. Next.js silently ignores it in route handlers (lines 14–18), making it dead code that can create false assumptions about request parsing. In App Router, manually parse the body using request.json(), request.text(), or request.formData() as needed.
Suggested fix
-export const config = {
- api: {
- bodyParser: false,
- },
-}📝 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 const config = { | |
| api: { | |
| bodyParser: false, | |
| }, | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/products/route.ts` around lines 14 - 18, Remove the dead export named
"config" (export const config = { api: { bodyParser: false } }) from the App
Router route handler because App Router ignores that Pages Router shape;
instead, update the route handler(s) (e.g., your GET/POST handler functions) to
explicitly parse the request body with request.json(), request.text(), or
request.formData() as appropriate and adjust any downstream code that assumed
bodyParser:false.
| const page = parseInt(searchParams.get('page') || '1', 10) | ||
| const limit = Math.min(parseInt(searchParams.get('limit') || '20', 10), 50) |
There was a problem hiding this comment.
Guard page/limit to prevent invalid pagination math and query args.
Line [30]-Line [31] can produce NaN, 0, or negative values, which then affect Line [62] (skip) and Line [84] (totalPages) incorrectly.
Suggested fix
-const page = parseInt(searchParams.get('page') || '1', 10)
-const limit = Math.min(parseInt(searchParams.get('limit') || '20', 10), 50)
+const rawPage = Number.parseInt(searchParams.get('page') || '1', 10)
+const rawLimit = Number.parseInt(searchParams.get('limit') || '20', 10)
+const page = Number.isFinite(rawPage) && rawPage > 0 ? rawPage : 1
+const limit =
+ Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 50) : 20Also applies to: 62-62, 84-85
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/products/route.ts` around lines 30 - 31, Validate and clamp parsed
pagination params to avoid NaN/zero/negative values: when reading page and limit
(where parseInt is currently used to set variables page and limit), coerce NaN
to defaults and clamp page to a minimum of 1 and limit to the range 1..50; then
use these sanitized values for computing skip (used around the skip calculation)
and totalPages (used where totalPages is computed) so downstream math is safe
and cannot produce 0/negative/NaN results.
| if (seller) { | ||
| filter.seller = seller | ||
| } else { | ||
| filter.$or = [{ is_sold: false }, { is_sold: true, show_when_sold: true }] | ||
| } |
There was a problem hiding this comment.
Public seller filter currently bypasses visibility constraints.
At Line [40]-Line [44], when seller is present, the query no longer applies unsold/sold-visible filtering, so hidden sold listings can be returned by this public endpoint.
Suggested fix
- if (seller) {
- filter.seller = seller
- } else {
- filter.$or = [{ is_sold: false }, { is_sold: true, show_when_sold: true }]
- }
+ const visibilityFilter = {
+ $or: [{ is_sold: false }, { is_sold: true, show_when_sold: true }],
+ }
+
+ if (seller) {
+ filter.$and = [{ seller }, visibilityFilter]
+ } else {
+ Object.assign(filter, visibilityFilter)
+ }📝 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.
| if (seller) { | |
| filter.seller = seller | |
| } else { | |
| filter.$or = [{ is_sold: false }, { is_sold: true, show_when_sold: true }] | |
| } | |
| const visibilityFilter = { | |
| $or: [{ is_sold: false }, { is_sold: true, show_when_sold: true }], | |
| } | |
| if (seller) { | |
| filter.$and = [{ seller }, visibilityFilter] | |
| } else { | |
| Object.assign(filter, visibilityFilter) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/products/route.ts` around lines 40 - 44, The current branch in
route.ts sets filter.seller when seller is present but omits the visibility
constraint, allowing hidden sold listings through; always apply a visibility
clause (e.g., an $or with {is_sold: false} and {is_sold: true, show_when_sold:
true}) regardless of whether seller is provided. Update the logic around the
filter variable (the seller check in route.ts) to compose the filter by merging
seller into the filter and also adding the visibilityClause (referencing seller,
filter, is_sold, and show_when_sold) so the public endpoint always enforces
unsold or sold-and-visible constraints.
|
@Rohank3 , your code is just hell an AI slop. We have decided not review it until you do it correctly. Your design doesn't have a plan, you should make the UI follow the website UI code but currently it is what the AI has made by itself. This is the last warning to you, otherwise all you contributions will be considered null and void. |
Thanks for the clarification — I’d like to explain my reasoning behind the changes. The existing UI works for basic listing, but when I started implementing additional features like:
it became difficult to extend the current layout cleanly without making it cluttered or inconsistent. Because of that, I explored a structured redesign to better support these features (modals, detailed views, action buttons, etc.). That said, I understand that I should not have introduced a full redesign without aligning with the project’s existing UI patterns first. I’m happy to:
Please let me know which approach you’d like me to take. |
Resolves #20
Description
Key additions include:
Productmodel with fields for title, description, price, Cloudinary image URL, and seller references.seller === req.user.id).GET), creating (POST), updating (PUT), marking as sold (PATCH), and deleting (DELETE).Live Demo (if any)
Note for Maintainer
Productmodel was strictly aligned with the requested schema.403 Forbiddenresponses properly fire when an unauthorized user attempts to modify someone else's product.Checkout
Summary by CodeRabbit
New Features
Chores