Feat/64 full text search - #74
Conversation
…lters, and pagination
|
Warning Review limit reached
Next review available in: 23 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
WalkthroughThe PR standardizes upload validation and response payloads, adds avatar-update authorization, and introduces indexed, paginated search for collections and educators with filtering, relevance sorting, caching, and integration tests. ChangesUpload hardening
Full-text search
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (2)
test/search.test.js (1)
59-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the short-query (<3 char) path and cache isolation between endpoints.
Every
qused here is ≥3 characters, so the short-query regex fallback (currently broken, seesearchService.js) and the/searchvs/educatorscache-key collision (seesearchRoutes.js) both ship untested. Once those are fixed, adding aq=Re(2-char) test and a same-params request against both/api/searchand/api/search/educatorswould lock in the fixes.Happy to draft these test cases if useful.
🤖 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 `@test/search.test.js` around lines 59 - 100, Extend the “Full-text search API” tests to cover a two-character query such as q=Re, asserting the short-query search returns a successful, relevant response. Add a cache-isolation test that sends identical query parameters to /api/search and /api/search/educators, then verifies each response retains its endpoint-specific result shape and data.src/models/Course.js (1)
51-52: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winFilter fields used by the new search service lack supporting indexes.
The search service filters courses/books by
categoryandprice, spaces bycategory/price, and educators byrole/interests— none of these are backed by a regular (non-text) index, only by the new text indexes on unrelated tokenized fields. As data grows, filter-only queries (e.g.?category=Cookingwith noq, as exercised intest/search.test.js) will fall back to full collection scans.
src/models/Course.js#L51-L52: addcourseSchema.index({ category: 1, price: 1 })(or separate indexes) to support the category/price filters.src/models/Space.js#L86-L87: addspaceSchema.index({ category: 1, price: 1 })for the same reason.src/models/User.js#L138-L139: adduserSchema.index({ role: 1, interests: 1 })to supportsearchEducators'srole/interestsfilters.As per path instructions for
src/models/**: "Flag schema changes that break existing documents, missing indexes for new query patterns, and TTL indexes that could delete durable records."🤖 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 `@src/models/Course.js` around lines 51 - 52, Add regular filter-supporting indexes alongside the existing text indexes: in src/models/Course.js lines 51-52, add a compound index on category and price; in src/models/Space.js lines 86-87, add the same category/price index; and in src/models/User.js lines 138-139, add a compound index on role and interests. These changes should support filter-only search queries without altering existing text-search indexes.Source: Path instructions
🤖 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 `@src/controllers/books/bookController.js`:
- Around line 30-32: Update the invalid upload responses in the book validation
path at src/controllers/books/bookController.js#L30-L32 and the avatar
validation path at src/controllers/userController.js#L17-L19 to use the
documented { success, message, data } envelope by adding data: null (or the
documented structured error payload) to both responses.
In `@src/controllers/searchController.js`:
- Around line 17-20: Update both error handlers in the search controller,
including the handler corresponding to the second reported location, to stop
returning err.message in the JSON response. Keep logger.error logging the caught
error server-side and preserve the existing 500 response with only the safe
public error fields.
In `@src/controllers/uploadController.js`:
- Around line 15-24: The upload response contract must use the shared { success,
message, data } envelope. In src/controllers/uploadController.js lines 15-24,
place signature metadata inside data, add an appropriate success message, and
return data: null with the failure message in the catch response. In
test/bookUpload.test.js lines 96-97, update the expected contract and assert the
file public ID through res.body.data.filePublicId.
- Around line 7-13: Restrict the signature generated by the upload controller’s
cloudinary.utils.api_sign_request call to the validated upload flow: either
route uploads through the existing Multer size and magic-byte checks, or sign
only explicitly allowed parameters backed by a restrictive Cloudinary preset and
size policy. Do not issue a reusable folder-only signature for direct-uploads
without enforcing those constraints.
In `@src/middlewares/errorHandler.js`:
- Around line 81-87: Update the MulterError normalization branch in the error
handler to set the response status to the failure value alongside statusCode
400, ensuring normalized upload errors are serialized as failures while
preserving the existing file-size message handling.
In `@src/middlewares/upload.js`:
- Around line 6-29: The invalid-upload paths in imageFilter and bookFilter
currently pass the intended message as MulterError’s field argument, so
errorHandler.js returns “Unexpected field.” Update errorHandler.js to map
LIMIT_UNEXPECTED_FILE to the intended custom message, or replace these errors
with APIError instances carrying those messages; preserve the existing
validation conditions and distinct image/book text.
In `@src/routes/searchRoutes.js`:
- Around line 9-23: Update searchCacheKey and its route usage so cache keys
distinguish the main search endpoint from the educators endpoint, using an
explicit route-specific namespace or key component. Add limit to the keyed query
parameters so requests with different page sizes do not share entries. Also
verify cacheMiddleware stores responses only when the request succeeds,
preventing transient error responses from being cached.
In `@src/routes/uploadRoutes.js`:
- Line 7: Add Jest coverage for the POST /signature route registered in
uploadRoutes, including rejection of unauthenticated requests and successful
signature generation for an authenticated request. Mock or isolate the signing
dependency so assertions verify the response and behavior without exposing or
comparing CLOUDINARY_API_SECRET contents.
In `@src/routes/userRoutes.js`:
- Around line 46-51: Add an ownership-or-admin authorization middleware between
protect and uploadImage.single in the "/update/:id" route, requiring
req.user._id to match req.params.id unless the user has admin privileges. Ensure
unauthorized requests are rejected before uploadImage runs, while authorized
requests continue to updateUser.
In `@src/services/search/searchService.js`:
- Around line 55-153: Validate and clamp the page and limit inputs in both
searchCollections and searchEducators before calculating skip or issuing
queries: ensure page is at least 1 and limit is within the enforced positive
maximum, then derive skip from the normalized values. Reuse the same
normalization behavior in both functions so zero, negative, non-numeric, or
oversized request values cannot produce negative skips or unlimited MongoDB
queries.
- Around line 8-14: Update getSearchQuery to accept the target field, escape
regex metacharacters before constructing the case-insensitive prefix RegExp, and
return the regex under that field instead of as a top-level MongoDB operator. In
searchModel, pass the appropriate field for each model, and apply the same
escaped, field-scoped handling to the educator short-query path.
- Around line 16-33: Update the course-specific search configuration and
handling around applyFilters so course rating filtering and sorting no longer
target the nonexistent top-level rating field. Either add and populate an
aggregated top-level Course rating consistently, or remove rating from course
filter/sort options; preserve rating search behavior for books.
In `@src/utils/fileValidation.js`:
- Around line 16-19: Remove the NODE_ENV === "test" bypass in the file
validation flow so fileValidation uses the same magic-byte detection and MIME
allowlist for tests and production. Update the test fixtures to provide minimal
valid binary signatures rather than arbitrary buffers, preserving rejection of
unsupported file types.
---
Nitpick comments:
In `@src/models/Course.js`:
- Around line 51-52: Add regular filter-supporting indexes alongside the
existing text indexes: in src/models/Course.js lines 51-52, add a compound index
on category and price; in src/models/Space.js lines 86-87, add the same
category/price index; and in src/models/User.js lines 138-139, add a compound
index on role and interests. These changes should support filter-only search
queries without altering existing text-search indexes.
In `@test/search.test.js`:
- Around line 59-100: Extend the “Full-text search API” tests to cover a
two-character query such as q=Re, asserting the short-query search returns a
successful, relevant response. Add a cache-isolation test that sends identical
query parameters to /api/search and /api/search/educators, then verifies each
response retains its endpoint-specific result shape and data.
🪄 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 Plus
Run ID: 2def0743-5693-4051-a2ce-48fb24d1fd80
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (21)
app.jspackage.jsonsrc/controllers/books/bookController.jssrc/controllers/searchController.jssrc/controllers/uploadController.jssrc/controllers/userController.jssrc/middlewares/errorHandler.jssrc/middlewares/upload.jssrc/models/Book.jssrc/models/Course.jssrc/models/Reel.jssrc/models/Space.jssrc/models/User.jssrc/routes/books/bookRoutes.jssrc/routes/searchRoutes.jssrc/routes/uploadRoutes.jssrc/routes/userRoutes.jssrc/services/search/searchService.jssrc/utils/fileValidation.jstest/bookUpload.test.jstest/search.test.js
| const signature = cloudinary.utils.api_sign_request( | ||
| { | ||
| timestamp, | ||
| folder: "direct-uploads", | ||
| }, | ||
| process.env.CLOUDINARY_API_SECRET | ||
| ); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files 'src/controllers/uploadController.js' 'src/routes/*' 'src/**/*.js' '.env.example' | sed 's#^`#-` #'
echo
echo "== uploadController outline =="
ast-grep outline src/controllers/uploadController.js --view expanded || true
echo
echo "== route/validation references =="
rg -n "api_sign_request|CLOUDINARY_API_SECRET|protect|multer|magic|upload" src/controllers src/routes src/middleware src -g '*.js' -g '*.mjs' || true
echo
echo "== uploadController lines =="
nl -ba src/controllers/uploadController.js | sed -n '1,220p'Repository: Deen-Bridge/dnb-backend
Length of output: 22097
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== uploadController.js =="
sed -n '1,220p' src/controllers/uploadController.js | cat -n
echo
echo "== uploadRoutes.js =="
sed -n '1,120p' src/routes/uploadRoutes.js | cat -n
echo
echo "== upload middleware =="
sed -n '1,220p' src/middlewares/upload.js | cat -n
echo
echo "== env validation =="
sed -n '1,120p' src/config/validateEnv.js | cat -n
echo
echo "== cloudinary util =="
sed -n '1,120p' src/utils/cloudinary.js | cat -n
echo
echo "== direct upload references =="
rg -n "direct-uploads|generateSignature|signature" src -g '*.js'Repository: Deen-Bridge/dnb-backend
Length of output: 8083
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== fileValidation.js =="
sed -n '1,220p' src/utils/fileValidation.js | cat -n
echo
echo "== where magic-byte validation is used =="
rg -n "validateMagicBytes|magic bytes|fileValidation" src -g '*.js'Repository: Deen-Bridge/dnb-backend
Length of output: 2737
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== cloudinary usage =="
rg -n "upload_preset|unsigned|signed upload|direct upload|resource_type|allowed_formats|max_file_size|eager" src -g '*.js'Repository: Deen-Bridge/dnb-backend
Length of output: 496
Scope Cloudinary signatures to the validated upload flow.
protect only blocks anonymous callers; this endpoint still hands out a reusable signature for folder=direct-uploads without the Multer size checks or magic-byte validation used by the server-side upload paths. Sign only the exact upload params you intend to allow, or move these uploads through the hardened server flow and enforce a restrictive Cloudinary preset/size policy.
🤖 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 `@src/controllers/uploadController.js` around lines 7 - 13, Restrict the
signature generated by the upload controller’s cloudinary.utils.api_sign_request
call to the validated upload flow: either route uploads through the existing
Multer size and magic-byte checks, or sign only explicitly allowed parameters
backed by a restrictive Cloudinary preset and size policy. Do not issue a
reusable folder-only signature for direct-uploads without enforcing those
constraints.
Source: Path instructions
| router.put( | ||
| "/update/:id", | ||
| protect, | ||
| upload.single("avatar"), | ||
| uploadImage.single("avatar"), | ||
| invalidateCacheMiddleware([`${CACHE_KEYS.USER}*`]), | ||
| updateUser |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Enforce ownership before accepting the upload.
protect authenticates but does not require req.user._id to match :id; the controller updates by req.params.id. Any signed-in user can therefore overwrite another profile. Add self-or-admin authorization before uploadImage so unauthorized requests cannot consume Cloudinary resources either.
As per path instructions, src/**/*.js requires missing auth/ownership checks to be flagged.
🤖 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 `@src/routes/userRoutes.js` around lines 46 - 51, Add an ownership-or-admin
authorization middleware between protect and uploadImage.single in the
"/update/:id" route, requiring req.user._id to match req.params.id unless the
user has admin privileges. Ensure unauthorized requests are rejected before
uploadImage runs, while authorized requests continue to updateUser.
Source: Path instructions
…resilience, and enhance security and validation middleware.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/controllers/searchController.js (2)
14-19: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winAlign both handlers with the required response contract.
These endpoints currently return
{ success, results, pagination }on success and{ success, error }on failure, rather than the required{ success, message, data }shape. This breaks clients that consume the standardized envelope; reconcile this with the PR’s statedresults/paginationcontract before merging.Also applies to: 31-36
🤖 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 `@src/controllers/searchController.js` around lines 14 - 19, Update both handlers in the search controller to use the required response envelope: return `{ success, message, data }` on success, placing the existing results and pagination payload under `data`, and return the corresponding message-based `{ success, message, data }` shape on failure. Preserve the existing search behavior and error status while aligning both success and catch paths.Source: Path instructions
8-14: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winGuard
qbefore trimming in search and cache-key generationsrc/controllers/searchController.js:8-14src/routes/searchRoutes.js:10-16
Repeated query params can arrive as an array, soq.trim()can throw and turn a bad request into a 500. Reject non-string values with 400, and normalizeqonce before reusing it in the handler andsearchCacheKey.🤖 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 `@src/controllers/searchController.js` around lines 8 - 14, Update the search handler and searchCacheKey flow to validate q is a string before trimming, returning a 400 response for array or other non-string query values. Normalize q once after validation, then reuse that normalized value for length checks, searchCollections, and cache-key generation instead of trimming independently.Source: Path instructions
♻️ Duplicate comments (2)
src/services/search/searchService.js (2)
65-67: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winNormalize pagination as positive safe integers in both search paths.
The current clamping permits fractional and non-finite values, which can make MongoDB reject
skip()orlimit()arguments.
src/services/search/searchService.js#L65-L67: validatepageandlimitbefore calculatingskip.src/services/search/searchService.js#L126-L129: reuse the same normalizer insearchEducators.As per path instructions, request input must be validated before database use.
🤖 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 `@src/services/search/searchService.js` around lines 65 - 67, Normalize page and limit as positive finite integers before database use, preserving the existing page and limit bounds. Add or reuse a shared normalizer near the first pagination flow, update src/services/search/searchService.js lines 65-67 to calculate skip from normalized values, and reuse it in searchEducators at lines 126-129; both sites require the same validation behavior.Source: Path instructions
101-104: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep sort capabilities model-specific.
The course configuration excludes
rating, but the sharedsortOptionis applied to everysearchModel. Therefore,type=courses&sort=ratingstill sends a rating sort to courses while Books retain rating support. Derive or validate sorting per model so unsupported course sorts are rejected or ignored.🤖 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 `@src/services/search/searchService.js` around lines 101 - 104, Update the course search configuration in the search service so its model-specific sort options cannot accept or apply the shared rating sort; preserve rating sorting for the Book searchModel call. Derive or validate sortOption separately for each model, rejecting or ignoring rating for courses while retaining supported course sorts such as category and price.
🤖 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 `@src/routes/userRoutes.js`:
- Around line 49-54: Update the authorization failure response in the user
profile middleware to include data: null alongside success and message,
preserving the required { success, message, data } shape.
- Around line 49-55: Extend test/upload.test.js with coverage for the PUT
/update/:id authorization middleware: add an authenticated owner case that
succeeds and an authenticated different-user case that returns 403, ensuring
both exercise the ownership check before uploadImage.single("avatar") runs.
In `@test/upload.test.js`:
- Around line 56-58: Update the secret assertion in the upload test to first
require that process.env.CLOUDINARY_API_SECRET is configured, then assert resStr
does not contain that actual secret value. Remove the "undefined" fallback so
the test fails when the environment variable is missing.
- Around line 6-12: Remove the hardcoded JWT, Cloudinary, and MongoDB values
from the test setup in upload.test.js, and read them from Jest/CI-provided
environment variables or a non-committed test environment file instead. Add the
required variable names and safe placeholder values to .env.example, while
preserving the existing test configuration behavior.
- Around line 6-14: Move the environment-variable initialization before
application loading by configuring Jest setupFiles/CI environment, or replace
the static app import with a dynamic await import after the assignments. Update
the test’s app-loading flow so app.js and transitive modules such as cloudinary
configuration observe the configured values.
---
Outside diff comments:
In `@src/controllers/searchController.js`:
- Around line 14-19: Update both handlers in the search controller to use the
required response envelope: return `{ success, message, data }` on success,
placing the existing results and pagination payload under `data`, and return the
corresponding message-based `{ success, message, data }` shape on failure.
Preserve the existing search behavior and error status while aligning both
success and catch paths.
- Around line 8-14: Update the search handler and searchCacheKey flow to
validate q is a string before trimming, returning a 400 response for array or
other non-string query values. Normalize q once after validation, then reuse
that normalized value for length checks, searchCollections, and cache-key
generation instead of trimming independently.
---
Duplicate comments:
In `@src/services/search/searchService.js`:
- Around line 65-67: Normalize page and limit as positive finite integers before
database use, preserving the existing page and limit bounds. Add or reuse a
shared normalizer near the first pagination flow, update
src/services/search/searchService.js lines 65-67 to calculate skip from
normalized values, and reuse it in searchEducators at lines 126-129; both sites
require the same validation behavior.
- Around line 101-104: Update the course search configuration in the search
service so its model-specific sort options cannot accept or apply the shared
rating sort; preserve rating sorting for the Book searchModel call. Derive or
validate sortOption separately for each model, rejecting or ignoring rating for
courses while retaining supported course sorts such as category and price.
🪄 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 Plus
Run ID: ad458873-1cc3-477f-9fda-a15cf9a22964
📒 Files selected for processing (12)
src/controllers/books/bookController.jssrc/controllers/searchController.jssrc/controllers/uploadController.jssrc/controllers/userController.jssrc/middlewares/errorHandler.jssrc/middlewares/upload.jssrc/routes/searchRoutes.jssrc/routes/userRoutes.jssrc/services/search/searchService.jssrc/utils/fileValidation.jstest/bookUpload.test.jstest/upload.test.js
💤 Files with no reviewable changes (1)
- src/utils/fileValidation.js
🚧 Files skipped from review as they are similar to previous changes (7)
- src/routes/searchRoutes.js
- src/controllers/userController.js
- src/middlewares/errorHandler.js
- src/controllers/uploadController.js
- src/middlewares/upload.js
- test/bookUpload.test.js
- src/controllers/books/bookController.js
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/controllers/books/bookController.js (2)
144-145: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winDo not expose raw internal error messages.
error.messagemay reveal database or implementation details. Log the original error server-side, but return a generic client message with the standard response envelope.🤖 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 `@src/controllers/books/bookController.js` around lines 144 - 145, Update the catch block in the book controller to log the original error server-side, then replace error.message in the res.status(500).json response with a generic client-safe message while preserving the existing success:false response envelope.
132-143: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winStandardize all changed responses on the documented envelope.
These newly changed responses should include
data: null(or the documented payload) alongsidesuccessandmessage.
src/controllers/books/bookController.js#L132-L143: adddatato delete-book 404, 403, and success responses.src/controllers/userController.js#L13-L16: adddatato the update authorization response.src/controllers/userController.js#L133-L136: adddatato the delete authorization response.As per path instructions, changed endpoints must use consistent response shapes:
{ success, message, data }.🤖 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 `@src/controllers/books/bookController.js` around lines 132 - 143, Standardize the response envelopes in the delete-book flow of bookController.js and the update/delete authorization responses in userController.js to include data alongside success and message. Add data: null to error and empty success responses, or the documented payload where applicable, while preserving the existing status codes and messages across all three affected sites.Source: Path instructions
🤖 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.
Outside diff comments:
In `@src/controllers/books/bookController.js`:
- Around line 144-145: Update the catch block in the book controller to log the
original error server-side, then replace error.message in the
res.status(500).json response with a generic client-safe message while
preserving the existing success:false response envelope.
- Around line 132-143: Standardize the response envelopes in the delete-book
flow of bookController.js and the update/delete authorization responses in
userController.js to include data alongside success and message. Add data: null
to error and empty success responses, or the documented payload where
applicable, while preserving the existing status codes and messages across all
three affected sites.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 69356c6f-9209-4713-8b97-59452123b86f
📒 Files selected for processing (3)
src/controllers/books/bookController.jssrc/controllers/userController.jssrc/models/User.js
🚧 Files skipped from review as they are similar to previous changes (1)
- src/models/User.js
…tion tests for user updates
…eat/64-full-text-search
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/routes/userRoutes.js (1)
49-54: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winPreserve the administrator authorization path.
This middleware allows only the exact profile owner; administrators cannot update another profile. Add the documented admin exception or reuse shared ownership/admin authorization middleware before
uploadImage.single("avatar"). This is the same unresolved admin-override finding from the earlier review.As per path instructions,
src/**/*.jsendpoints must enforce appropriate authorization checks.🤖 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 `@src/routes/userRoutes.js` around lines 49 - 54, Update the authorization middleware before uploadImage.single("avatar") to allow administrators to update other profiles while retaining the existing owner-only check for regular users. Reuse the shared ownership/admin authorization middleware if available; otherwise add the documented administrator exception in the middleware containing the req.user._id and req.params.id comparison.Source: Path instructions
🤖 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 `@test/jest.setup.js`:
- Around line 1-7: Remove the hardcoded JWT, MongoDB, and Cloudinary values from
the Jest setup configuration and read them from injected CI/Jest environment
variables or an ignored .env.test file. Validate that the required variables are
present rather than assigning fallback secrets, while preserving the existing
NODE_ENV and PORT setup. Document the required configuration keys in
.env.example.
---
Duplicate comments:
In `@src/routes/userRoutes.js`:
- Around line 49-54: Update the authorization middleware before
uploadImage.single("avatar") to allow administrators to update other profiles
while retaining the existing owner-only check for regular users. Reuse the
shared ownership/admin authorization middleware if available; otherwise add the
documented administrator exception in the middleware containing the req.user._id
and req.params.id comparison.
🪄 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 Plus
Run ID: 02fbc4f4-be16-475c-8bd3-497703737570
📒 Files selected for processing (5)
jest.config.jssrc/routes/userRoutes.jstest/bookUpload.test.jstest/jest.setup.jstest/upload.test.js
💤 Files with no reviewable changes (1)
- test/bookUpload.test.js
…variables and increase test timeout
Title
feat(search): implement full-text search API with text indexes, ranking, and pagination
Context
This addresses the unoptimized and unpaginated search implementation in
searchAllwhich relied on leading wildcard regexes, causing full-collection scans without index utilization. The new implementation establishes appropriate MongoDB text indexes across our core models, solving performance bottlenecks while also introducing missing feature sets including filtering, relevance ranking, and paginated blocks.Implementation Details
Course(title^5, description, category),Book(title^5, description, category),Space(title, description, category),User(name, bio, interests), andReel(description).searchService.jsto manage multi-collection querying using$textand projecting{ score: { $meta: "textScore" } }for accurate relevance sort ordering.minPrice,maxPrice,free,category, andminRating.searchController.jsto strip away bare array returning patterns, standardizing responses with{ success, results, pagination }.type=educators(and/api/search/educators) search tailored for discovering tutors via names, bios, or interests, while strictly stripping private user fields from payloads.searchRoutes.jscache generation mechanisms to securely leverage route caching.Verification
freeparameters.password,email, and other sensitive parameters are forcibly stripped when matching educators.npm test -- test/search.test.jscompletely passing without CI integration problems.closes #64
Summary by CodeRabbit