Skip to content

Merge staging changes to production - 23/03/26#95

Open
eiidoubleyuwes wants to merge 35 commits intomainfrom
staging
Open

Merge staging changes to production - 23/03/26#95
eiidoubleyuwes wants to merge 35 commits intomainfrom
staging

Conversation

@eiidoubleyuwes
Copy link
Copy Markdown
Collaborator

@eiidoubleyuwes eiidoubleyuwes commented Mar 23, 2026

Summary by CodeRabbit

  • New Features

    • Added scanned-tickets tracking to record scanner, attendee, event and ticket scans.
    • Event descriptions now support extended text for more detailed event information.
  • Bug Fixes

    • Ticket quantity is decremented only after successful payment confirmation.
    • More robust payment/transaction validation and conditional processing to prevent inconsistent updates.
    • Improved attendee scan flow with clearer status responses (too early, expired, already scanned) and duplicate-scan handling.
  • Chores

    • Updated package dependencies.

dependabot bot and others added 24 commits February 27, 2026 17:32
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>
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
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

The 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

Cohort / File(s) Summary
Payment Controller & Consumer
Controllers/Purchase.controller.js, Middleware/Veribroke_sdk_recieve.js
Moved ticket decrement to after successful payment request in purchase controller; consumer now fetches transaction by request_id, proceeds only for PENDING transactions, creates attendee on success, and restores/increments ticket_quantity on failure.
Attendee scanning flow
Controllers/Attendee.controller.js, Repositories/Scanned_tickets.repository.js
Added find-or-create scan logic in controller and new repository functions for scanned_tickets (getAll..., createScannedTicketRepository, findOrCreateScannedTicketRepository).
New Model & Exports
Models/Scanned_tickets.model.js, Models/index.js
Added scanned_tickets Sequelize model with paranoid deletes, unique composite index; exported ScannedTickets from models index.
Event model change
Models/Event.model.js
Changed event_description column type from DataTypes.STRING to DataTypes.TEXT.
Dependencies
package.json
Bumped multer ^2.0.2 → ^2.1.1 and sequelize ^6.37.7 → ^6.37.8.
Misc. controller updates
Controllers/Attendee.controller.js
Adjusted scan authorization/status responses, day-level event-date checks, and UniqueConstraintError handling returning ALREADY_SCANNED.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Eugene600
  • IamMuuo
  • ordo-chao

Poem

🐰 I hopped where payments softly tread,

waited for the coins to spread,
only when the bells rang true,
did I nibble tickets, just a few —
now scanners cheer and events are fed.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Merge staging changes to production - 23/03/26' is vague and generic, using non-descriptive terms that don't convey meaningful information about the specific changes made. Replace with a descriptive title summarizing the main changes, such as 'Add scanned tickets tracking and refactor payment/attendee flow' to clearly communicate the primary purpose.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch staging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@eiidoubleyuwes eiidoubleyuwes changed the title Staging Merge staging chnages to production - 23/03/26 Mar 23, 2026
@eiidoubleyuwes eiidoubleyuwes changed the title Merge staging chnages to production - 23/03/26 Merge staging changes to production - 23/03/26 Mar 23, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 | 🔴 Critical

Free ticket path does not decrement ticket quantity.

When ticket.ticket_price === 0, an attendee is created immediately without decrementing ticket_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 | 🟠 Major

Race condition: ticket decrement after unconfirmed message publish.

sendPaymentRequest publishes 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 implement confirmSelect() or publisher confirms anywhere.

Additionally, the payment request and ticket decrement are not wrapped in a database transaction. If the server crashes between sendPaymentRequest succeeding (line 172) and updateTicketRepository completing (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:

  1. Enable publisher confirms in sendPaymentRequest using channel.confirmSelect() and wait for confirmation before resolving
  2. Wrap the transaction creation and ticket decrement in a single sequelize.transaction() to ensure atomicity
  3. 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 unused Op import.

Op is 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

📥 Commits

Reviewing files that changed from the base of the PR and between fbc6332 and cbdceb5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • Controllers/Purchase.controller.js
  • Middleware/Veribroke_sdk_recieve.js
  • Models/Event.model.js
  • package.json

Comment on lines +76 to +80
await updateTicketRepository(transaction.ticket_id, {
ticket_quantity: Sequelize.literal(
`ticket_quantity + ${Number(transaction.ticket_quantity)}`
)
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines 95 to 100
} catch (error) {
if (msg.fields.redelivered) {
channel.ack(msg); // prevent infinite loop
channel.ack(msg);
} else {
channel.nack(msg, false, true);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

dependabot bot and others added 2 commits March 26, 2026 10:10
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>
ordo-chao and others added 9 commits March 27, 2026 12:56
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…
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 | 🟠 Major

Missing validation for attendeeId request parameter.

eventId is validated at line 165, but attendeeId is extracted from req.body at line 161 and used at line 183 without any presence check. If attendeeId is 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: UniqueConstraintError catch is defensive but likely unnecessary.

The findOrCreate call at line 217 should atomically handle the race condition. This catch block would only trigger if there's a bug in Sequelize's findOrCreate or if another code path calls createScannedTicketRepository directly.

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 for WRONG_EVENT may confuse API consumers.

The response uses 200 OK with { status: "WRONG_EVENT" } when the attendee is not found for the given event. Typically, 404 Not Found or 422 Unprocessable Entity would be more appropriate for this error condition. The same pattern is used for TOO_EARLY, EXPIRED, and ALREADY_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 redundant try/catch blocks that only rethrow.

All four functions wrap their logic in try/catch blocks that simply throw 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: createScannedTicketRepository bypasses deduplication logic.

This function inserts directly without checking for existing records, which could cause UniqueConstraintError if called for an already-scanned ticket. The codebase correctly uses findOrCreateScannedTicketRepository instead (per the context snippet), but having this function exported invites misuse.

Consider removing it or documenting that findOrCreateScannedTicketRepository should 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 define references to 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: withDeleted scope may not fully override defaultScope exclusions.

Sequelize merges scopes rather than replacing them. The defaultScope excludes ['created_at', 'updated_at', 'deleted_at'], and withDeleted only adds back deleted_at. When using .scope('withDeleted'), queries will still exclude created_at and updated_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 for ScannedTickets.

The model defines foreign keys (event_id, attendee_id, ticket_id, scanner_id) but no Sequelize associations are wired in this file. Other models like Transaction and Attendee have explicit belongsTo relationships defined here.

While the current repository functions use raw where clauses and don't rely on associations, adding them would enable eager loading with include and 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

📥 Commits

Reviewing files that changed from the base of the PR and between cbdceb5 and 585870f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • Controllers/Attendee.controller.js
  • Models/Scanned_tickets.model.js
  • Models/index.js
  • Repositories/Scanned_tickets.repository.js

Comment on lines +201 to 215
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" });
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants