Merge staging changes to production - 23/03/26#95
Merge staging changes to production - 23/03/26#95eiidoubleyuwes wants to merge 35 commits intomainfrom
Conversation
Bumps [dottie](https://github.com/mickhansen/dottie.js) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/mickhansen/dottie.js/releases) - [Commits](mickhansen/dottie.js@v2.0.6...v2.0.7) --- updated-dependencies: - dependency-name: dottie dependency-version: 2.0.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together. Updates `minimatch` from 10.1.1 to 10.2.4 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v10.1.1...v10.2.4) Updates `minimatch` from 3.1.2 to 3.1.5 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v10.1.1...v10.2.4) Updates `minimatch` from 9.0.5 to 9.0.9 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v10.1.1...v10.2.4) --- updated-dependencies: - dependency-name: minimatch dependency-version: 10.2.4 dependency-type: indirect - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect - dependency-name: minimatch dependency-version: 9.0.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…tie-2.0.7 Build(deps): Bump dottie from 2.0.6 to 2.0.7
Fix: changed from string to text
Bumps [underscore](https://github.com/jashkenas/underscore) from 1.13.7 to 1.13.8. - [Commits](jashkenas/underscore@1.13.7...1.13.8) --- updated-dependencies: - dependency-name: underscore dependency-version: 1.13.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…kets" This reverts commit d1db9bd.
Feat fix veribroke issues
Bumps [multer](https://github.com/expressjs/multer) from 2.0.2 to 2.1.1. - [Release notes](https://github.com/expressjs/multer/releases) - [Changelog](https://github.com/expressjs/multer/blob/main/CHANGELOG.md) - [Commits](expressjs/multer@v2.0.2...v2.1.1) --- updated-dependencies: - dependency-name: multer dependency-version: 2.1.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [sequelize](https://github.com/sequelize/sequelize) from 6.37.7 to 6.37.8. - [Release notes](https://github.com/sequelize/sequelize/releases) - [Changelog](https://github.com/sequelize/sequelize/blob/main/CHANGELOG.md) - [Commits](sequelize/sequelize@v6.37.7...v6.37.8) --- updated-dependencies: - dependency-name: sequelize dependency-version: 6.37.8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
…cess-and-pending Feat: veribroke issue of sucess and pending
…tity-after-db-transactions Fix: only decreasing ticket quantity after db transactions
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) and [@aws-sdk/xml-builder](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages-internal/xml-builder). These dependencies needed to be updated together. Updates `fast-xml-parser` from 5.3.6 to 5.5.8 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](NaturalIntelligence/fast-xml-parser@v5.3.6...v5.5.8) Updates `@aws-sdk/xml-builder` from 3.972.5 to 3.972.15 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages-internal/xml-builder/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/HEAD/packages-internal/xml-builder) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.5.8 dependency-type: indirect - dependency-name: "@aws-sdk/xml-builder" dependency-version: 3.972.15 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…ti-88c8261ef1 Build(deps): Bump fast-xml-parser and @aws-sdk/xml-builder
…uelize-6.37.8 Build(deps): Bump sequelize from 6.37.7 to 6.37.8
…ter-2.1.1 Build(deps): Bump multer from 2.0.2 to 2.1.1
…erscore-1.13.8 Build(deps): Bump underscore from 1.13.7 to 1.13.8
…ti-f5f34deeac Build(deps): Bump minimatch
📝 WalkthroughWalkthroughThe PR moves ticket-quantity decrement in the purchase flow to occur only after a successful payment request, restructures the payment webhook consumer to fetch and gate on transaction status and conditionally create attendees or restore ticket quantities, adds a scanned_tickets model/repository and exports it, changes event_description to TEXT, and bumps two dependencies. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant PurchaseController
participant PaymentSDK
participant DB as Database
participant Consumer
Client->>PurchaseController: POST /purchase (ticket request)
PurchaseController->>PaymentSDK: sendPaymentRequest(paymentData)
PaymentSDK-->>PurchaseController: payment response (success)
PurchaseController->>DB: updateTicketRepository (decrement quantity)
PurchaseController-->>Client: 200 OK
PaymentSDK->>Consumer: webhook/callback (request_id, status, metadata)
Consumer->>DB: getTransactionByIdRepository(request_id)
alt transaction exists and status == PENDING
Consumer->>DB: updateTransactionRepository(status, failure_reason?)
alt status == SUCCESS
Consumer->>DB: createAttendeeRepository(using transaction fields)
else status != SUCCESS
Consumer->>DB: updateTicketRepository(increment quantity via Sequelize.literal)
end
end
Consumer-->>PaymentSDK: ack/nack
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Controllers/Purchase.controller.js (2)
59-63:⚠️ Potential issue | 🔴 CriticalFree ticket path does not decrement ticket quantity.
When
ticket.ticket_price === 0, an attendee is created immediately without decrementingticket_quantity. This allows unlimited free ticket registrations beyond available inventory.🐛 Proposed fix to decrement tickets for free registrations
if (ticket.ticket_price === 0) { + await updateTicketRepository(ticket_id, { + ticket_quantity: Sequelize.literal(`ticket_quantity - ${ticket_quantity}`) + }); const attendee = await createAttendeeRepository({ user_id, event_id, ticket_id, ticket_quantity }); const duration = Number(process.hrtime.bigint() - start) / 1000; logs(duration, "INFO", req.ip, req.method, "Successfully registered for event", req.path, 201, req.headers["user-agent"]); return res.status(201).json({ message: "Successfully registered for event", attendee_id : attendee.id });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Purchase.controller.js` around lines 59 - 63, The free-ticket branch (ticket.ticket_price === 0) creates an attendee via createAttendeeRepository without decrementing ticket.ticket_quantity, allowing oversell; modify this branch to atomically check and decrement ticket.ticket_quantity (using the existing ticket repository/update method or by adding an atomic decrement function) before calling createAttendeeRepository, return an out-of-stock error if quantity is 0, and ensure the update and attendee creation happen inside a transaction or with proper concurrency control to avoid race conditions; update the success path to use the decremented state and log the correct response.
171-178:⚠️ Potential issue | 🟠 MajorRace condition: ticket decrement after unconfirmed message publish.
sendPaymentRequestpublishes to RabbitMQ without publisher confirms—channel.publish()at line 29 in Middleware/Veribroke_sdk_push.js only buffers the message locally. If RabbitMQ fails to persist the message, the function still resolves successfully (line 32-33 close and return), tickets get decremented, and the SDK callback never arrives to restore them. The codebase does not implementconfirmSelect()or publisher confirms anywhere.Additionally, the payment request and ticket decrement are not wrapped in a database transaction. If the server crashes between
sendPaymentRequestsucceeding (line 172) andupdateTicketRepositorycompleting (line 176-178), the transaction record exists in the database but ticket quantity is never decremented—creating inconsistent state and potential for overselling.Separately, the free ticket path (line 59-60) creates an attendee without decrementing ticket quantity, unlike the paid ticket flow.
Consider:
- Enable publisher confirms in
sendPaymentRequestusingchannel.confirmSelect()and wait for confirmation before resolving- Wrap the transaction creation and ticket decrement in a single
sequelize.transaction()to ensure atomicity- Add a reconciliation job to detect and fix stale PENDING transactions where tickets were not decremented
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Purchase.controller.js` around lines 171 - 178, sendPaymentRequest currently publishes via channel.publish without publisher confirms (see Middleware/Veribroke_sdk_push.js — channel.publish at line ~29) so resolve occurs before RabbitMQ persistence; modify sendPaymentRequest to call channel.confirmSelect() and wait for the broker confirmation (e.g., waitForConfirms or equivalent) before resolving or throwing on NACK, then only proceed in Purchase.controller.js after confirmed. Also wrap the transaction creation and ticket decrement (the code calling sendPaymentRequest and updateTicketRepository) inside a single sequelize.transaction() so creating the payment/transaction record and decrementing ticket_quantity via updateTicketRepository happen atomically; on failure roll back and do not decrement. Finally, align the free-ticket path (the createAttendee/free ticket code path) to decrement ticket_quantity the same way, and add a periodic reconciliation job to scan PENDING transactions and restore ticket_quantity when a confirmed publish/transaction mismatch is detected.
🧹 Nitpick comments (2)
Controllers/Purchase.controller.js (1)
43-47: Clean up empty lines left from code removal.These consecutive empty lines appear to be artifacts from removing the previous ticket decrement logic. Consider removing the excess whitespace for cleaner code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Purchase.controller.js` around lines 43 - 47, Remove the surplus consecutive empty lines left by the removed ticket-decrement logic in Controllers/Purchase.controller.js: collapse multiple blank lines into a single newline where the old logic was removed (ensure there are no more than one consecutive blank line), save the file and run the project's formatter/linter so the spacing conforms to code style; target the area around the purchase controller export/function where the decrement logic used to live.Middleware/Veribroke_sdk_recieve.js (1)
5-5: Remove unusedOpimport.
Opis imported from Sequelize but never used in this file.♻️ Proposed fix
-import { Op, Sequelize } from "sequelize"; +import { Sequelize } from "sequelize";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Middleware/Veribroke_sdk_recieve.js` at line 5, Remove the unused Op import from the import statement: update the existing import line that currently imports "Op, Sequelize" so it only imports Sequelize (i.e., remove the symbol Op) to eliminate the unused variable warning and keep imports minimal; verify there are no other references to Op in the file before committing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Middleware/Veribroke_sdk_recieve.js`:
- Around line 95-100: In the catch block inside
Middleware/Veribroke_sdk_recieve.js, add error logging and stop silently
acknowledging redelivered messages: log the caught error (e.g., with
console.error or processLogger.error) including context (msg properties and
error.stack) and change the redelivery branch that currently calls
channel.ack(msg) to instead nack the message without requeue (channel.nack(msg,
false, false)) or publish it to your dead-letter queue handler so the failed
message is not lost; keep the existing channel.nack(msg, false, true) for
first-time failures if you want retries.
- Around line 76-80: The current code interpolates transaction.ticket_quantity
into Sequelize.literal, allowing invalid values (NaN/null/undefined) to produce
bad SQL; before calling updateTicketRepository, validate and coerce
transaction.ticket_quantity to a safe finite integer (e.g., const qty =
Number(transaction.ticket_quantity); if (!Number.isFinite(qty) || isNaN(qty))
throw or default to 0) and then avoid string interpolation by using a safe
increment API or SQL function: call your repository with a numeric increment
(e.g., use Model.increment('ticket_quantity', { by: qty, where: { id:
transaction.ticket_id }, transaction }) or pass Sequelize.fn('COALESCE',
Sequelize.col('ticket_quantity'), 0) + qty via a parameterized approach) so
references to updateTicketRepository, Sequelize.literal and
transaction.ticket_quantity are replaced with a validated qty and a
non-interpolated update.
---
Outside diff comments:
In `@Controllers/Purchase.controller.js`:
- Around line 59-63: The free-ticket branch (ticket.ticket_price === 0) creates
an attendee via createAttendeeRepository without decrementing
ticket.ticket_quantity, allowing oversell; modify this branch to atomically
check and decrement ticket.ticket_quantity (using the existing ticket
repository/update method or by adding an atomic decrement function) before
calling createAttendeeRepository, return an out-of-stock error if quantity is 0,
and ensure the update and attendee creation happen inside a transaction or with
proper concurrency control to avoid race conditions; update the success path to
use the decremented state and log the correct response.
- Around line 171-178: sendPaymentRequest currently publishes via
channel.publish without publisher confirms (see Middleware/Veribroke_sdk_push.js
— channel.publish at line ~29) so resolve occurs before RabbitMQ persistence;
modify sendPaymentRequest to call channel.confirmSelect() and wait for the
broker confirmation (e.g., waitForConfirms or equivalent) before resolving or
throwing on NACK, then only proceed in Purchase.controller.js after confirmed.
Also wrap the transaction creation and ticket decrement (the code calling
sendPaymentRequest and updateTicketRepository) inside a single
sequelize.transaction() so creating the payment/transaction record and
decrementing ticket_quantity via updateTicketRepository happen atomically; on
failure roll back and do not decrement. Finally, align the free-ticket path (the
createAttendee/free ticket code path) to decrement ticket_quantity the same way,
and add a periodic reconciliation job to scan PENDING transactions and restore
ticket_quantity when a confirmed publish/transaction mismatch is detected.
---
Nitpick comments:
In `@Controllers/Purchase.controller.js`:
- Around line 43-47: Remove the surplus consecutive empty lines left by the
removed ticket-decrement logic in Controllers/Purchase.controller.js: collapse
multiple blank lines into a single newline where the old logic was removed
(ensure there are no more than one consecutive blank line), save the file and
run the project's formatter/linter so the spacing conforms to code style; target
the area around the purchase controller export/function where the decrement
logic used to live.
In `@Middleware/Veribroke_sdk_recieve.js`:
- Line 5: Remove the unused Op import from the import statement: update the
existing import line that currently imports "Op, Sequelize" so it only imports
Sequelize (i.e., remove the symbol Op) to eliminate the unused variable warning
and keep imports minimal; verify there are no other references to Op in the file
before committing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3fbc4674-ad54-473c-b8ab-63b7ec57268c
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
Controllers/Purchase.controller.jsMiddleware/Veribroke_sdk_recieve.jsModels/Event.model.jspackage.json
| await updateTicketRepository(transaction.ticket_id, { | ||
| ticket_quantity: Sequelize.literal( | ||
| `ticket_quantity + ${Number(transaction.ticket_quantity)}` | ||
| ) | ||
| }); |
There was a problem hiding this comment.
SQL injection risk with Sequelize.literal.
transaction.ticket_quantity is interpolated directly into a SQL expression. While Number() is applied, if ticket_quantity is NaN, null, or an unexpected type, this could produce invalid SQL or unexpected behavior. Additionally, Number(undefined) returns NaN, which would result in ticket_quantity + NaN.
🛡️ Proposed fix to validate ticket_quantity
if (!success) {
+ const qty = Number(transaction.ticket_quantity);
+ if (!Number.isFinite(qty) || qty <= 0) {
+ console.error(`Invalid ticket_quantity for transaction ${request_id}: ${transaction.ticket_quantity}`);
+ } else {
await updateTicketRepository(transaction.ticket_id, {
- ticket_quantity: Sequelize.literal(
- `ticket_quantity + ${Number(transaction.ticket_quantity)}`
- )
+ ticket_quantity: Sequelize.literal(`ticket_quantity + ${qty}`)
});
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Middleware/Veribroke_sdk_recieve.js` around lines 76 - 80, The current code
interpolates transaction.ticket_quantity into Sequelize.literal, allowing
invalid values (NaN/null/undefined) to produce bad SQL; before calling
updateTicketRepository, validate and coerce transaction.ticket_quantity to a
safe finite integer (e.g., const qty = Number(transaction.ticket_quantity); if
(!Number.isFinite(qty) || isNaN(qty)) throw or default to 0) and then avoid
string interpolation by using a safe increment API or SQL function: call your
repository with a numeric increment (e.g., use
Model.increment('ticket_quantity', { by: qty, where: { id: transaction.ticket_id
}, transaction }) or pass Sequelize.fn('COALESCE',
Sequelize.col('ticket_quantity'), 0) + qty via a parameterized approach) so
references to updateTicketRepository, Sequelize.literal and
transaction.ticket_quantity are replaced with a validated qty and a
non-interpolated update.
| } catch (error) { | ||
| if (msg.fields.redelivered) { | ||
| channel.ack(msg); // prevent infinite loop | ||
| channel.ack(msg); | ||
| } else { | ||
| channel.nack(msg, false, true); | ||
| } |
There was a problem hiding this comment.
Silent error handling loses diagnostic information.
Errors caught at line 95 are not logged, making debugging difficult. Additionally, redelivered messages are acknowledged without processing (line 97), which could lead to data loss if the first attempt partially succeeded before failing.
🔧 Proposed fix to add error logging
} catch (error) {
+ console.error(`❌ Error processing MPESA callback:`, error.message, error.stack);
if (msg.fields.redelivered) {
channel.ack(msg);
} else {
channel.nack(msg, false, true);
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Middleware/Veribroke_sdk_recieve.js` around lines 95 - 100, In the catch
block inside Middleware/Veribroke_sdk_recieve.js, add error logging and stop
silently acknowledging redelivered messages: log the caught error (e.g., with
console.error or processLogger.error) including context (msg properties and
error.stack) and change the redelivery branch that currently calls
channel.ack(msg) to instead nack the message without requeue (channel.nack(msg,
false, false)) or publish it to your dead-letter queue handler so the failed
message is not lost; keep the existing channel.nack(msg, false, true) for
first-time failures if you want retries.
Bumps and [picomatch](https://github.com/micromatch/picomatch). These dependencies needed to be updated together. Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@2.3.1...2.3.2) Updates `picomatch` from 4.0.3 to 4.0.4 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](micromatch/picomatch@2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ti-bf05dc1ecf Build(deps): Bump picomatch
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ogging improvements Agent-Logs-Url: https://github.com/opencrafts-io/sherehe/sessions/de48aa08-d6a9-4ebc-85ac-4f4aa7e29fb0 Co-authored-by: ordo-chao <160326107+ordo-chao@users.noreply.github.com>
Feat: added scanned tickets table and not allowing of tickets before…
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Controllers/Attendee.controller.js (1)
161-169:⚠️ Potential issue | 🟠 MajorMissing validation for
attendeeIdrequest parameter.
eventIdis validated at line 165, butattendeeIdis extracted fromreq.bodyat line 161 and used at line 183 without any presence check. IfattendeeIdis missing or undefined, the query at line 183 will behave unexpectedly—potentially returning null or an unintended record.Proposed fix
if (!eventId) { const duration = Number(process.hrtime.bigint() - start) / 1000; logs(duration, "WARN", req.ip, req.method, "Missing event ID", req.path, 400, req.headers["user-agent"]); return res.status(400).json({ error: "Event ID is required" }); } + + if (!attendeeId) { + const duration = Number(process.hrtime.bigint() - start) / 1000; + logs(duration, "WARN", req.ip, req.method, "Missing attendee ID", req.path, 400, req.headers["user-agent"]); + return res.status(400).json({ error: "Attendee ID is required" }); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Attendee.controller.js` around lines 161 - 169, Add the same presence validation for attendeeId that exists for eventId: check req.body.attendeeId (the attendeeId variable) after you extract it and before it’s used in the query at line ~183, and if missing call logs(...) with the same parameters (duration, "WARN", req.ip, req.method, "Missing attendee ID", req.path, 400, req.headers["user-agent"]) then return res.status(400).json({ error: "Attendee ID is required" }); this mirrors the existing eventId validation and prevents running the query with an undefined attendeeId.
🧹 Nitpick comments (8)
Controllers/Attendee.controller.js (3)
241-243:UniqueConstraintErrorcatch is defensive but likely unnecessary.The
findOrCreatecall at line 217 should atomically handle the race condition. This catch block would only trigger if there's a bug in Sequelize'sfindOrCreateor if another code path callscreateScannedTicketRepositorydirectly.Keeping it as a safety net is fine, but consider logging when this branch is hit to detect unexpected behavior.
Add logging for visibility
if (error instanceof UniqueConstraintError) { + logs(duration, "WARN", req.ip, req.method, "UniqueConstraintError caught (unexpected)", req.path, 200, req.headers["user-agent"]); return res.status(200).json({ status: "ALREADY_SCANNED" }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Attendee.controller.js` around lines 241 - 243, The UniqueConstraintError catch is defensive; keep the existing res.status(200).json({ status: "ALREADY_SCANNED" }) behavior but add logging so we can detect unexpected hits: inside the catch that checks "error instanceof UniqueConstraintError" call the existing logger (or processLogger/console) to record the error with context including the attendee/ticket identifiers and mention the function createScannedTicketRepository and the findOrCreate flow; ensure the log shows the full error stack/message so we can investigate if Sequelize misbehaves or if createScannedTicketRepository is being invoked elsewhere.
207-215: Single-day scanning window may be too restrictive.The current logic only allows scanning when
today === eventDay. Multi-day events or events that span midnight would fail this check. Additionally, there's no grace period for late arrivals or early setup.If the business logic intentionally restricts to exact-day scanning, this is fine. Otherwise, consider adding a configurable buffer or supporting event end dates.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Attendee.controller.js` around lines 207 - 215, The check that only allows scanning when today === eventDay (using variables today and eventDay) is too strict for multi-day events or grace periods; update the logic in the controller (around the block referencing today and eventDay in Attendee.controller.js) to either compare against an event end date (e.g., eventEnd or event.endDate) or add a configurable buffer (minutes/days) to allow a start/end window (e.g., allow today >= eventStart - buffer && today <= eventEnd + buffer); ensure you replace the exact-day early/expired responses with window-based checks and keep the same status values or add new ones if needed.
186-192: Returning HTTP 200 forWRONG_EVENTmay confuse API consumers.The response uses
200 OKwith{ status: "WRONG_EVENT" }when the attendee is not found for the given event. Typically,404 Not Foundor422 Unprocessable Entitywould be more appropriate for this error condition. The same pattern is used forTOO_EARLY,EXPIRED, andALREADY_SCANNED.If this is an intentional API design choice (e.g., for mobile clients that parse status codes differently), this is acceptable. Otherwise, consider using semantic HTTP status codes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Controllers/Attendee.controller.js` around lines 186 - 192, The controller returns HTTP 200 for error statuses like "WRONG_EVENT" which is misleading; update the response to use a semantic HTTP status (e.g., change res.status(200).json({ status: "WRONG_EVENT" }) to res.status(404).json(...) or 422 if you prefer validation semantics) in the Attendee.controller.js branch that checks if (!result), and apply the same change for the other branches that return "TOO_EARLY", "EXPIRED", and "ALREADY_SCANNED"; ensure you still call logs(...) with the correct status code and keep the existing payload shape.Repositories/Scanned_tickets.repository.js (2)
3-13: Remove redundanttry/catchblocks that only rethrow.All four functions wrap their logic in
try/catchblocks that simplythrow error. This adds no value—errors will propagate naturally without the wrapper. It only adds noise and indentation.Proposed simplification
export const getAllScannedTicketsByEventIdRepository = async (id) => { - try { - return await ScannedTickets.findAll({ + return ScannedTickets.findAll({ where: { event_id: id } }); - } catch (error) { - throw error; - } }Apply similar simplification to the other functions.
Also applies to: 15-25, 27-34, 36-53
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Repositories/Scanned_tickets.repository.js` around lines 3 - 13, The repository functions (e.g., getAllScannedTicketsByEventIdRepository, getAllScannedTicketsByUserIdRepository, getScannedTicketByTicketIdRepository, and createScannedTicketRepository) contain try/catch blocks that only rethrow the caught error—remove these redundant try/catch wrappers and let errors propagate naturally; update each function to directly return the result of the Sequelize calls (e.g., return ScannedTickets.findAll(...) or return ScannedTickets.create(...)) without surrounding try/catch, keeping existing where/attributes logic intact.
27-34:createScannedTicketRepositorybypasses deduplication logic.This function inserts directly without checking for existing records, which could cause
UniqueConstraintErrorif called for an already-scanned ticket. The codebase correctly usesfindOrCreateScannedTicketRepositoryinstead (per the context snippet), but having this function exported invites misuse.Consider removing it or documenting that
findOrCreateScannedTicketRepositoryshould be preferred for scan operations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Repositories/Scanned_tickets.repository.js` around lines 27 - 34, The exported createScannedTicketRepository function inserts without deduplication and can trigger UniqueConstraintError; remove this function from the module (or make it internal/unexported) and update any callers to use findOrCreateScannedTicketRepository instead; search for and replace usages of createScannedTicketRepository with findOrCreateScannedTicketRepository (or wrap createScannedTicketRepository to delegate to findOrCreateScannedTicketRepository) and update the module's export list so only deduplicating API is publicly available.Models/Scanned_tickets.model.js (2)
12-36: Missing foreign key references at the database level.The UUID fields (
attendee_id,scanner_id,event_id,ticket_id) don't definereferencesto their respective tables. While this works functionally, adding FK constraints provides database-level referential integrity and prevents orphaned records.Example for one field
event_id: { type: DataTypes.UUID, - allowNull: false + allowNull: false, + references: { + model: 'events', + key: 'id' + } },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Models/Scanned_tickets.model.js` around lines 12 - 36, Add database-level foreign key constraints for the UUID fields in the Scanned_tickets model: update the attendee_id, scanner_id, event_id, and ticket_id attribute definitions to include a references object pointing to their respective target tables/models (e.g., Attendees, Scanners, Events, Tickets) and a target key of "id"; also add appropriate onDelete/onUpdate behaviors (e.g., CASCADE or RESTRICT) to enforce referential integrity. Locate and modify the attributes named attendee_id, scanner_id, event_id, and ticket_id in the Scanned_tickets model to include the references and constraint options so the DB will enforce the relationships.
49-56:withDeletedscope may not fully overridedefaultScopeexclusions.Sequelize merges scopes rather than replacing them. The
defaultScopeexcludes['created_at', 'updated_at', 'deleted_at'], andwithDeletedonly adds backdeleted_at. When using.scope('withDeleted'), queries will still excludecreated_atandupdated_at.If the intent is to retrieve all timestamp columns, consider:
Proposed fix
scopes: { withDeleted: { - attributes: { include: ['deleted_at'] } + attributes: { exclude: [] } } },Or use
unscoped()when you need all columns.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Models/Scanned_tickets.model.js` around lines 49 - 56, The withDeleted scope currently only adds deleted_at but because Sequelize merges scopes the defaultScope exclusions still apply; update scopes.withDeleted to explicitly include the timestamp fields you want returned (e.g., set attributes: { include: ['deleted_at', 'created_at', 'updated_at'] } in the withDeleted scope) so created_at and updated_at are returned when using .scope('withDeleted'), or alternatively document using .unscoped() where you need every column.Models/index.js (1)
8-8: Consider adding Sequelize associations forScannedTickets.The model defines foreign keys (
event_id,attendee_id,ticket_id,scanner_id) but no Sequelize associations are wired in this file. Other models likeTransactionandAttendeehave explicitbelongsTorelationships defined here.While the current repository functions use raw
whereclauses and don't rely on associations, adding them would enable eager loading withincludeand provide referential integrity at the ORM level if needed later.Also applies to: 52-52
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Models/index.js` at line 8, ScannedTickets defines FK columns (event_id, attendee_id, ticket_id, scanner_id) but no Sequelize associations; import the related models (Event, Attendee, Ticket, Scanner/User as appropriate) into Models/index.js and wire up ScannedTickets.belongsTo(Event, { foreignKey: 'event_id' }), ScannedTickets.belongsTo(Attendee, { foreignKey: 'attendee_id' }), ScannedTickets.belongsTo(Ticket, { foreignKey: 'ticket_id' }) and ScannedTickets.belongsTo(ScannerOrUserModel, { foreignKey: 'scanner_id' }); also consider adding reciprocal hasMany associations on the referenced models (e.g., Event.hasMany(ScannedTickets, { foreignKey: 'event_id' })) so eager loading with include works and ORM-level relations are available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Controllers/Attendee.controller.js`:
- Around line 201-215: The current comparison uses UTC-midnight normalization
(eventDate/eventDay and today) which can shift calendar-day boundaries for
organizers in non-UTC timezones; update the logic in Attendee.controller.js
(near eventDateRaw, eventDate, eventDay, today) to compare calendar dates in the
event's intended timezone: either parse eventDateRaw as a local/date-only value
(treat "YYYY-MM-DD" as the event's local date) and compare date-only strings
(e.g., eventDate.toISOString().slice(0,10) or formatted YYYY-MM-DD) or use a
timezone-aware library (e.g., Luxon/DateTime.fromISO with the event's timezone)
to compute eventDay and today in that timezone before comparing; ensure you use
the same timezone source (event.timezone or documented server timezone)
consistently and replace the UTC setUTCHours normalization with the chosen
approach.
---
Outside diff comments:
In `@Controllers/Attendee.controller.js`:
- Around line 161-169: Add the same presence validation for attendeeId that
exists for eventId: check req.body.attendeeId (the attendeeId variable) after
you extract it and before it’s used in the query at line ~183, and if missing
call logs(...) with the same parameters (duration, "WARN", req.ip, req.method,
"Missing attendee ID", req.path, 400, req.headers["user-agent"]) then return
res.status(400).json({ error: "Attendee ID is required" }); this mirrors the
existing eventId validation and prevents running the query with an undefined
attendeeId.
---
Nitpick comments:
In `@Controllers/Attendee.controller.js`:
- Around line 241-243: The UniqueConstraintError catch is defensive; keep the
existing res.status(200).json({ status: "ALREADY_SCANNED" }) behavior but add
logging so we can detect unexpected hits: inside the catch that checks "error
instanceof UniqueConstraintError" call the existing logger (or
processLogger/console) to record the error with context including the
attendee/ticket identifiers and mention the function
createScannedTicketRepository and the findOrCreate flow; ensure the log shows
the full error stack/message so we can investigate if Sequelize misbehaves or if
createScannedTicketRepository is being invoked elsewhere.
- Around line 207-215: The check that only allows scanning when today ===
eventDay (using variables today and eventDay) is too strict for multi-day events
or grace periods; update the logic in the controller (around the block
referencing today and eventDay in Attendee.controller.js) to either compare
against an event end date (e.g., eventEnd or event.endDate) or add a
configurable buffer (minutes/days) to allow a start/end window (e.g., allow
today >= eventStart - buffer && today <= eventEnd + buffer); ensure you replace
the exact-day early/expired responses with window-based checks and keep the same
status values or add new ones if needed.
- Around line 186-192: The controller returns HTTP 200 for error statuses like
"WRONG_EVENT" which is misleading; update the response to use a semantic HTTP
status (e.g., change res.status(200).json({ status: "WRONG_EVENT" }) to
res.status(404).json(...) or 422 if you prefer validation semantics) in the
Attendee.controller.js branch that checks if (!result), and apply the same
change for the other branches that return "TOO_EARLY", "EXPIRED", and
"ALREADY_SCANNED"; ensure you still call logs(...) with the correct status code
and keep the existing payload shape.
In `@Models/index.js`:
- Line 8: ScannedTickets defines FK columns (event_id, attendee_id, ticket_id,
scanner_id) but no Sequelize associations; import the related models (Event,
Attendee, Ticket, Scanner/User as appropriate) into Models/index.js and wire up
ScannedTickets.belongsTo(Event, { foreignKey: 'event_id' }),
ScannedTickets.belongsTo(Attendee, { foreignKey: 'attendee_id' }),
ScannedTickets.belongsTo(Ticket, { foreignKey: 'ticket_id' }) and
ScannedTickets.belongsTo(ScannerOrUserModel, { foreignKey: 'scanner_id' }); also
consider adding reciprocal hasMany associations on the referenced models (e.g.,
Event.hasMany(ScannedTickets, { foreignKey: 'event_id' })) so eager loading with
include works and ORM-level relations are available.
In `@Models/Scanned_tickets.model.js`:
- Around line 12-36: Add database-level foreign key constraints for the UUID
fields in the Scanned_tickets model: update the attendee_id, scanner_id,
event_id, and ticket_id attribute definitions to include a references object
pointing to their respective target tables/models (e.g., Attendees, Scanners,
Events, Tickets) and a target key of "id"; also add appropriate
onDelete/onUpdate behaviors (e.g., CASCADE or RESTRICT) to enforce referential
integrity. Locate and modify the attributes named attendee_id, scanner_id,
event_id, and ticket_id in the Scanned_tickets model to include the references
and constraint options so the DB will enforce the relationships.
- Around line 49-56: The withDeleted scope currently only adds deleted_at but
because Sequelize merges scopes the defaultScope exclusions still apply; update
scopes.withDeleted to explicitly include the timestamp fields you want returned
(e.g., set attributes: { include: ['deleted_at', 'created_at', 'updated_at'] }
in the withDeleted scope) so created_at and updated_at are returned when using
.scope('withDeleted'), or alternatively document using .unscoped() where you
need every column.
In `@Repositories/Scanned_tickets.repository.js`:
- Around line 3-13: The repository functions (e.g.,
getAllScannedTicketsByEventIdRepository, getAllScannedTicketsByUserIdRepository,
getScannedTicketByTicketIdRepository, and createScannedTicketRepository) contain
try/catch blocks that only rethrow the caught error—remove these redundant
try/catch wrappers and let errors propagate naturally; update each function to
directly return the result of the Sequelize calls (e.g., return
ScannedTickets.findAll(...) or return ScannedTickets.create(...)) without
surrounding try/catch, keeping existing where/attributes logic intact.
- Around line 27-34: The exported createScannedTicketRepository function inserts
without deduplication and can trigger UniqueConstraintError; remove this
function from the module (or make it internal/unexported) and update any callers
to use findOrCreateScannedTicketRepository instead; search for and replace
usages of createScannedTicketRepository with findOrCreateScannedTicketRepository
(or wrap createScannedTicketRepository to delegate to
findOrCreateScannedTicketRepository) and update the module's export list so only
deduplicating API is publicly available.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 804e13ab-5824-43f6-b261-09789e8bb03d
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
Controllers/Attendee.controller.jsModels/Scanned_tickets.model.jsModels/index.jsRepositories/Scanned_tickets.repository.js
| const eventDate = new Date(eventDateRaw); | ||
| const now = new Date(); | ||
|
|
||
| const eventDay = new Date(eventDate).setUTCHours(0, 0, 0, 0); | ||
| const today = new Date(now).setUTCHours(0, 0, 0, 0); | ||
|
|
||
| if (today < eventDay) { | ||
| return res.status(200).json({ | ||
| status: "TOO_EARLY", | ||
| }); | ||
| } | ||
|
|
||
| if (today > eventDay) { | ||
| return res.status(200).json({ status: "EXPIRED" }); | ||
| } |
There was a problem hiding this comment.
UTC-based day comparison may cause timezone issues for event organizers.
The logic normalizes both dates to UTC midnight for comparison. If an event's event_date is stored as a local date (e.g., "2026-03-26" meaning March 26 in the event's timezone), comparing against UTC midnight could cause scanning to be blocked or allowed on the wrong calendar day depending on the user's timezone offset.
For example, an event on March 26 in UTC+5 would have scanning blocked after 7 PM on March 26 local time (when UTC hits March 27).
Consider whether the event date should be interpreted in the event's local timezone or documenting that all event dates are UTC-based.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Controllers/Attendee.controller.js` around lines 201 - 215, The current
comparison uses UTC-midnight normalization (eventDate/eventDay and today) which
can shift calendar-day boundaries for organizers in non-UTC timezones; update
the logic in Attendee.controller.js (near eventDateRaw, eventDate, eventDay,
today) to compare calendar dates in the event's intended timezone: either parse
eventDateRaw as a local/date-only value (treat "YYYY-MM-DD" as the event's local
date) and compare date-only strings (e.g., eventDate.toISOString().slice(0,10)
or formatted YYYY-MM-DD) or use a timezone-aware library (e.g.,
Luxon/DateTime.fromISO with the event's timezone) to compute eventDay and today
in that timezone before comparing; ensure you use the same timezone source
(event.timezone or documented server timezone) consistently and replace the UTC
setUTCHours normalization with the chosen approach.
Summary by CodeRabbit
New Features
Bug Fixes
Chores