Conversation
…lidations, and routes
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 20 minutes and 8 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughInitializes an appointment-booking Node.js API with project configs, Express server entrypoint, MongoDB connection, Winston logging, shared utilities, and two feature modules (appointments and availability) providing Mongoose models, services, controllers, routes, and Zod validation. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as Appointment<br/>Controller
participant Service as Appointment<br/>Service
participant AvailabilityModel as Availability<br/>Model
participant AppointmentModel as Appointment<br/>Model
participant DB as MongoDB
Client->>Controller: POST /appointments (payload)
Controller->>Service: createAppointment(data)
Service->>AvailabilityModel: find({providerId,date,startTime,endTime})
AvailabilityModel->>DB: query availability
DB-->>AvailabilityModel: availability (or null)
AvailabilityModel-->>Service: result
alt Slot not available
Service-->>Controller: throw "Slot is not available"
else
Service->>AppointmentModel: find({providerId,date,startTime,endTime, status != 'cancelled'})
AppointmentModel->>DB: query appointments
DB-->>AppointmentModel: appointment (or null)
AppointmentModel-->>Service: result
alt Slot already booked
Service-->>Controller: throw "This slot is already booked"
else
Service->>AppointmentModel: save(new Appointment)
AppointmentModel->>DB: insert document
DB-->>AppointmentModel: saved appointment
Service-->>Controller: return created appointment (201)
end
end
Controller-->>Client: JSON response / error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (11)
src/server.js (1)
4-6: Consider using the project's logger instead ofconsole.log.The project has a logger utility (
src/shared/utils/logger.js) used elsewhere. For consistency:+import logger from "./shared/utils/logger.js"; + app.listen(PORT, () => { - console.log(`server is running on http://localhost:${PORT} `); + logger.info(`Server is running on http://localhost:${PORT}`); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server.js` around lines 4 - 6, Replace the direct console.log in the app.listen callback with the project's logger to keep logs consistent: import the logger utility (symbol name logger) at the top of the file, then use logger.info (or the appropriate level) instead of console.log in the app.listen(PORT, ...) callback and include the same message with PORT; ensure no other console.log remains in that startup block.src/modules/availability/availability.validation.js (1)
3-8: Consider addingstartTime<endTimevalidation.The schema validates that times are non-empty strings but doesn't enforce that
startTimeis beforeendTime. A.refine()at the object level could prevent invalid time ranges from being submitted.💡 Suggested enhancement
export const createAvailabilitySchema = z.object({ providerId: z.string().min(1, "Provider ID is required"), - date: z.string().refine(val => !isNaN(Date.parse(val)), "Invalid date"), + date: z.string().refine(val => !isNaN(Date.parse(val)), { message: "Invalid date" }), startTime: z.string().min(1, "Start time is required"), endTime: z.string().min(1, "End time is required") -}); +}).refine(data => data.startTime < data.endTime, { + message: "Start time must be before end time", + path: ["endTime"] +});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/availability/availability.validation.js` around lines 3 - 8, The current createAvailabilitySchema validates providerId, date, startTime and endTime but doesn't ensure startTime is before endTime; add an object-level .refine on createAvailabilitySchema that parses the date plus startTime/endTime (or parses times into minutes) and returns true only when start < end, and supply a clear error message (e.g., "Start time must be before end time") and optionally set the error path to ["startTime"] so the validation failure surfaces on the startTime field..gitignore (1)
2-4: Redundant.enventry.Line 3 is unnecessary since
.env*on line 2 already matches.env. Consider removing it for clarity.node_modules/ .env* -.env !.env.example🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 2 - 4, The .gitignore contains a redundant entry: ".env" is duplicated because ".env*" already matches it; remove the explicit ".env" line (the entry currently listed as .env on line 3) so only ".env*" and the negation "!.env.example" remain, leaving the pattern set clear and non-redundant.src/modules/appointments/appointment.validation.js (1)
3-10: Consider adding time range and date format validations.Similar to the availability schema, this lacks
startTime < endTimevalidation. Additionally, consider using Zod 4's standalonez.iso.date()orz.iso.datetime()for stricter date validation instead of relying on the permissiveDate.parse().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/appointments/appointment.validation.js` around lines 3 - 10, The createAppointmentSchema currently uses a permissive Date.parse and lacks a time-order check; replace the date field validation with Zod's stricter ISO datetime/date validator (e.g., z.string().datetime() or z.string().refine using z.iso.datetime()) for the date field, and add a schema-level .refine on createAppointmentSchema that parses the date+startTime and date+endTime (or parses startTime/endTime as "HH:mm") and ensures startTime < endTime, returning clear error messages referencing date, startTime, and endTime when validation fails.src/shared/config/env.config.js (1)
7-7:transform(Number)can produceNaNfor invalid input.If
PORTis set to a non-numeric string (e.g.,"abc"),transform(Number)silently returnsNaN. Consider adding validation after the transform:- PORT: z.string().default('5000').transform(Number), + PORT: z.string().default('5000').transform(Number).pipe(z.number().int().positive()),This applies to
BCRYPT_ROUNDSand the rate limit values as well.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/config/env.config.js` at line 7, The current zod schema uses transform(Number) for PORT (and similarly for BCRYPT_ROUNDS and rate limit values) which can produce NaN for invalid strings; replace transform(Number) with a safe coercion/validation pattern such as z.preprocess or z.coerce.number() followed by numeric checks (e.g., .int().positive().min(1).max(65535) for PORT, and appropriate .int().min/.max for BCRYPT_ROUNDS and rate limits) or throw a clear error in the transform so invalid values are rejected instead of silently becoming NaN; update the PORT, BCRYPT_ROUNDS, and rate limit schema entries to use this validated coercion pattern.src/modules/availability/availability.model.js (1)
3-9: Consider adding indexes for query performance and uniqueness constraint.The service queries availability by
providerIdsorted bydateandstartTime. Adding a compound index would improve query performance. Additionally, consider a unique constraint to prevent duplicate availability slots.📈 Suggested schema improvements
const availabilitySchema = new mongoose.Schema({ providerId: { type: mongoose.Schema.Types.ObjectId, ref: 'Provider', required: true }, date: { type: Date, required: true }, startTime: { type: String, required: true }, endTime: { type: String, required: true }, createdAt: { type: Date, default: Date.now } }); + +// Index for efficient provider availability queries +availabilitySchema.index({ providerId: 1, date: 1, startTime: 1 }); + +// Prevent duplicate availability slots +availabilitySchema.index( + { providerId: 1, date: 1, startTime: 1, endTime: 1 }, + { unique: true } +);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/availability/availability.model.js` around lines 3 - 9, Add a compound index to the availability schema to improve queries that filter by providerId and sort by date and startTime, and enforce uniqueness to prevent duplicate slots: update availabilitySchema (the mongoose.Schema instance) to call availabilitySchema.index({ providerId: 1, date: 1, startTime: 1 }, { unique: true }) so queries that use providerId/date/startTime are faster and duplicate availability entries are rejected; ensure the index key order matches how you query/sort in the service.src/modules/appointments/appointment.model.js (1)
3-12: Add indexes for query performance and double-booking prevention.The service queries appointments by
userId. Common queries will also likely filter byproviderIdanddate. Consider adding indexes and a unique constraint to prevent double-booking the same provider slot.📈 Suggested schema improvements
const appointmentSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, providerId: { type: mongoose.Schema.Types.ObjectId, ref: 'Provider', required: true }, serviceId: { type: mongoose.Schema.Types.ObjectId, ref: 'Service', required: true }, date: { type: Date, required: true }, startTime: { type: String, required: true }, endTime: { type: String, required: true }, status: { type: String, enum: ['pending','confirmed','cancelled'], default: 'pending' }, createdAt: { type: Date, default: Date.now } }); + +// Index for user appointment queries +appointmentSchema.index({ userId: 1, date: -1 }); + +// Index for provider schedule queries +appointmentSchema.index({ providerId: 1, date: 1, startTime: 1 }); + +// Prevent double-booking (exclude cancelled appointments in application logic) +appointmentSchema.index( + { providerId: 1, date: 1, startTime: 1 }, + { unique: true, partialFilterExpression: { status: { $ne: 'cancelled' } } } +);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/appointments/appointment.model.js` around lines 3 - 12, Add indexes on the appointment schema to improve query performance and prevent double-booking: define an index on userId for fast user queries, a compound index on providerId and date for common provider/date lookups, and a unique compound index on providerId, date and startTime (or providerId, date, startTime, endTime if you prefer strict slot uniqueness) to prevent two appointments occupying the same provider slot; implement these by calling appointmentSchema.index(...) (referencing appointmentSchema) before exporting the model.src/shared/utils/helpers.js (1)
21-29: Logging in constructor causes every error instantiation to log.Creating an
AppErroralways logs the error, even if the error is later handled gracefully or used for flow control. This may result in excessive or misleading log entries. Consider moving the logging to the error handler middleware instead.♻️ Proposed fix
export class AppError extends Error { constructor(message, statusCode = 500, isOperational = true) { super(message); this.statusCode = statusCode; this.isOperational = isOperational; this.timestamp = new Date().toISOString(); Error.captureStackTrace(this, this.constructor); - logger.error(`${statusCode} - ${message}`); } }Log the error in your global error handler middleware instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/utils/helpers.js` around lines 21 - 29, The AppError constructor currently calls logger.error(...) which causes every instantiation to be logged; remove the logging call from the AppError constructor (the class and its constructor function) so the constructor only sets message, statusCode, isOperational, timestamp, and capture stack trace; instead ensure the global error handler middleware (where errors are formatted and sent) performs logger.error(...) using the AppError instance details (statusCode, message, timestamp, stack) so logging happens once when the error is handled.src/shared/config/db.config.js (2)
3-4: Redundantdotenv/configimport.
MONGODB_URIis imported fromenv.config.js, which already loadsdotenv/configat the top. The import on line 4 is redundant and can be removed.♻️ Proposed fix
import { MONGODB_URI } from "./env.config.js"; -import "dotenv/config"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/config/db.config.js` around lines 3 - 4, Remove the redundant top-level import of "dotenv/config" in this module: since MONGODB_URI is imported from env.config.js which already loads dotenv/config, delete the extra import statement (the standalone "dotenv/config" import) so only the MONGODB_URI import remains; ensure no other code relies on the removed side-effect import and run tests/lint to confirm.
27-38: Event listeners registered after successful connection only.The
mongoose.connection.on(...)handlers are set up inside the try block aftermongoose.connect()succeeds. If connection fails, these monitoring handlers won't be registered. Consider moving them beforemongoose.connect()so reconnection events are captured from the start.♻️ Proposed fix
const connectDB = async () =>{ + // Register event handlers before connecting + mongoose.connection.on("connected", () => { + logger.info("MongoDB connected") + }) + mongoose.connection.on("error", (err) => { + logger.error(`MongoDB error: ${err}`) + }) + mongoose.connection.on("disconnected", () => { + logger.warn("MongoDB disconnected") + }) + mongoose.connection.on("reconnected", () => { + logger.info("MongoDB reconnected") + }) + try{ const conn = await mongoose.connect(MONGODB_URI, { // ... options }) logger.info(`MONGODB Connected: ${conn.connection.host}`) - - // monitoring - mongoose.connection.on("connected" , () => { - logger.info("MongoDB connected") - }) - // ... rest of handlers🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/config/db.config.js` around lines 27 - 38, Move the mongoose connection event registrations so they run before attempting to connect: register the listeners using mongoose.connection.on("connected", ...), .on("error", ...), .on("disconnected", ...), and .on("reconnected", ...) prior to calling mongoose.connect(...) (the code that currently lives inside the try block). This ensures the handlers (registered on mongoose.connection) are attached even if mongoose.connect() fails or reconnects later; after moving them, keep the existing log messages and leave the mongoose.connect(...) call inside the try/catch as before.src/shared/utils/logger.js (1)
26-43: Shallow redaction may miss nested sensitive fields.The comment states "To Handle nested sensitive data," but
redactSensitiveonly checks immediate properties ofinfoandinfo.metadata. Nested structures like{ user: { password: "secret" } }or{ body: { token: "xyz" } }won't be redacted.Consider a recursive implementation if nested redaction is required.
♻️ Proposed recursive redaction
const redactFormat = winston.format((info) => { - // To Handle nested sensitive data const redactSensitive = (obj) => { if (!obj || typeof obj !== 'object') return obj; + if (Array.isArray(obj)) return obj.map(redactSensitive); const safe = { ...obj }; - if (safe.password) safe.password = '[REDACTED]'; - if (safe.token) safe.token = '[REDACTED]'; - if (safe.authorization) safe.authorization = '[REDACTED]'; + for (const key of Object.keys(safe)) { + if (['password', 'token', 'authorization'].includes(key.toLowerCase())) { + safe[key] = '[REDACTED]'; + } else if (typeof safe[key] === 'object') { + safe[key] = redactSensitive(safe[key]); + } + } return safe; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/utils/logger.js` around lines 26 - 43, The redactFormat middleware currently only redacts top-level keys; update the redactSensitive function used by redactFormat to recursively traverse objects and arrays and redact any nested keys named "password", "token", or "authorization" (and variants if desired), including inside info.metadata and any nested structures; perform the traversal in a safe way (create/return a cloned object or mutate a shallow copy) to avoid prototype pollution and ensure all nested occurrences are replaced with "[REDACTED]".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.env.example:
- Around line 6-7: The .env.example lists MONGODB_URI_PROD but env.config.js's
Zod schema does not validate it; either remove MONGODB_URI_PROD from
.env.example or add it to the Zod schema in env.config.js (e.g., add a
MONGODB_URI_PROD entry alongside MONGODB_URI with the same validation rules such
as z.string().nonempty() or an optional z.string() if allowed) and update any
places that read process.env.MONGODB_URI_PROD to use the new schema key.
In `@package.json`:
- Line 30: Replace the unmaintained dependency "express-rate-limiter" in
package.json with the correct maintained package "express-rate-limit", run your
package manager to update lockfiles, and update any imports/usages that
reference "express-rate-limiter" to "express-rate-limit" (e.g., change
require/import strings and ensure the middleware creation uses the standard
rateLimit API). Also search for symbol usages like rateLimiter middleware
instantiation and adjust to the "express-rate-limit" API if needed, then
reinstall and commit updated package.json and lockfile.
In `@src/modules/appointments/appointment.controller.js`:
- Around line 7-9: The controller currently catches all exceptions and responds
with HTTP 400 in the catch blocks (seen where catch(err) { res.status(400)... })
which masks real server errors; change those catch blocks in the appointment
controller (the functions wrapping try/catch that currently call
res.status(400).json(...)) to either (A) forward the error to centralized
middleware by calling next(err) so a global error handler can map status codes,
or (B) inspect the error (e.g., err.name, err instanceof ValidationError or
database error) and return appropriate codes (400 for validation/client errors,
500 for server/db errors) instead of always 400; update all catch locations (the
ones around lines 7-9 and 17-19) to follow one of these approaches for
consistent handling.
In `@src/modules/appointments/appointment.routes.js`:
- Around line 9-12: Protect the appointment routes by adding the authentication
middleware (e.g., auth or requireAuth) before controllers so POST '/' uses auth
+ validate(createAppointmentSchema) then
appointmentController.createAppointment, and GET '/user/:userId' uses auth and
an authorization check that ensures req.user.id === userId or has admin role
before calling appointmentController.getUserAppointments; also add a param
validation middleware (or use validate with a param schema) to ensure the route
param userId is a valid MongoDB ObjectId (e.g., validateObjectId or Joi/ObjectId
check) before reaching the controller.
In `@src/modules/appointments/appointment.service.js`:
- Around line 4-24: createAppointment is using data.date (a string) to query
Date fields, so convert it to a Date object and validate it before querying and
saving: parse data.date into const parsedDate = new Date(data.date), throw an
error if isNaN(parsedDate.getTime()), use parsedDate in both
Availability.findOne and Appointment.findOne queries (replace data.date), assign
data.date = parsedDate before constructing new Appointment(data) and calling
appointment.save(), so queries match the models' Date fields.
In `@src/modules/availability/availability.controller.js`:
- Around line 7-9: The catch blocks in availability.controller.js currently
always return 400 (res.status(400).json...) which misclassifies server errors;
update each catch in the controller functions (the catch blocks that currently
call res.status(400).json({ message: err.message })) to inspect the error (e.g.,
err.name / instanceof checks for validation errors like ValidationError,
CastError, or custom input validation) and return 400 for client/validation
errors but 500 for unexpected/database errors; preserve the err.message in the
JSON body and ensure functions such as the controller methods that handle
get/create/update/delete availability use this conditional status mapping.
In `@src/modules/availability/availability.routes.js`:
- Around line 8-12: Implement and export authentication helpers in
src/shared/middleware/auth.middleware.js (e.g., authenticate and
authorizeProvider) so they validate JWT/session and check provider
ownership/roles; then protect the availability routes by adding these middleware
to the router (wrap router.post('/', validate(createAvailabilitySchema),
availabilityController.createAvailability) and router.get('/:providerId',
availabilityController.getAvailability) so they call authenticate and, where
appropriate, authorizeProvider before the controller). Finally register the
availability router inside src/routes/app.js by importing the availability
routes and mounting them on an appropriate path (e.g., app.use('/availability',
availabilityRouter)) so the endpoints are reachable.
In `@src/modules/availability/availability.service.js`:
- Around line 3-14: In createAvailability, convert the incoming data.date to a
Date object before using it in Availability.findOne and before constructing the
new Availability to ensure consistent types; update the function
(createAvailability) to parse/normalize data.date (e.g., new Date(data.date) and
validate it) and then use that normalized Date in the findOne query and when
creating the Availability instance to prevent mismatches with the model's Date
field.
In `@src/modules/availability/availability.validation.js`:
- Line 5: The schema's "date" field currently uses Zod 3 refine syntax
(z.string().refine(val => !isNaN(Date.parse(val)), "Invalid date")); update the
refine call to Zod 4 shape by passing an options object with a message (e.g.,
z.string().refine(val => !isNaN(Date.parse(val)), { message: "Invalid date" }))
or use an equivalent z.preprocess/zod transform; locate the "date" property and
the z.string().refine invocation in the schema and replace the second-argument
string with an object containing message to restore compatibility with Zod
4.3.6.
In `@src/modules/availability/index.js`:
- Around line 1-4: The index file uses export * which does not re-export default
exports, so the default-exported Availability model from availability.model.js
is not exposed; update the barrel in index.js to explicitly re-export the
default (e.g., export the default as Availability) from
'./availability.model.js' while leaving the existing named re-exports
(availability.controller.js, availability.service.js,
availability.validation.js) intact so consumers can import the Availability
model from the module root.
In `@src/shared/config/db.config.js`:
- Around line 40-42: Fix the typos in the shutdown-related comment and log
inside the shutdown async function: change the comment "graceful shotdown" to
"graceful shutdown" and update the logger.info message in the shutdown function
from "Recieved" to "Received" (refer to the shutdown function and its
logger.info call to locate the change).
In `@src/shared/config/env.config.js`:
- Around line 28-35: In the catch block handling validation errors, avoid
double-logging z.ZodError by returning or using an else after detecting
z.ZodError: when (error instanceof z.ZodError) log the per-field messages via
error.errors and then return (or put the generic logger.error("Unknown
Environment Validation Error", error) inside an else) so it does not run for
ZodErrors; also fix the typo "Unkown" to "Unknown" and ensure process.exit(1)
still runs for all error paths.
In `@src/shared/utils/helpers.js`:
- Around line 85-90: The scheduleReminder function currently uses setTimeout (in
scheduleReminder) which is lost on server restarts; replace this ephemeral
scheduling with a persistent job queue (e.g., Bull or Agenda) and a DB
table/collection for reminder records: enqueue a job instead of calling
setTimeout in scheduleReminder(appointmentDate, callback), persist the reminder
payload and job id when creating the job, and on application startup rehydrate
pending reminders by loading unsent records and scheduling them in the queue (or
resuming jobs via the queue's built-in persistence). Ensure cancel/reschedule
flows update both the DB record and the queued job using the stored job id.
- Line 32: Fix the typo in the comment heading "GENERAL ASYNC HANLDER" by
changing "HANLDER" to "HANDLER" so the comment reads "GENERAL ASYNC HANDLER";
locate the string in src/shared/utils/helpers.js (the comment line containing
"GENERAL ASYNC HANLDER") and update it accordingly.
- Line 40: The generateOTP function uses Math.random() which is not
cryptographically secure; replace it with Node's crypto.randomInt to produce a
6-digit OTP. Import the crypto module and change generateOTP to call
crypto.randomInt(100000, 1000000) (upper bound exclusive), convert the result to
a string (add zero-padding if you alter bounds) and return that value; update
any references to generateOTP accordingly.
In `@src/shared/utils/logger.js`:
- Around line 16-22: The colors mapping uses an unsupported value "purple" for
the debug level; update the colors object (the debug property in the colors
constant) to a valid Winston color such as "blue" (or "magenta"/"brightMagenta")
so Winston's colorizer accepts it—locate the colors constant and replace the
debug: "purple" entry with debug: "blue".
---
Nitpick comments:
In @.gitignore:
- Around line 2-4: The .gitignore contains a redundant entry: ".env" is
duplicated because ".env*" already matches it; remove the explicit ".env" line
(the entry currently listed as .env on line 3) so only ".env*" and the negation
"!.env.example" remain, leaving the pattern set clear and non-redundant.
In `@src/modules/appointments/appointment.model.js`:
- Around line 3-12: Add indexes on the appointment schema to improve query
performance and prevent double-booking: define an index on userId for fast user
queries, a compound index on providerId and date for common provider/date
lookups, and a unique compound index on providerId, date and startTime (or
providerId, date, startTime, endTime if you prefer strict slot uniqueness) to
prevent two appointments occupying the same provider slot; implement these by
calling appointmentSchema.index(...) (referencing appointmentSchema) before
exporting the model.
In `@src/modules/appointments/appointment.validation.js`:
- Around line 3-10: The createAppointmentSchema currently uses a permissive
Date.parse and lacks a time-order check; replace the date field validation with
Zod's stricter ISO datetime/date validator (e.g., z.string().datetime() or
z.string().refine using z.iso.datetime()) for the date field, and add a
schema-level .refine on createAppointmentSchema that parses the date+startTime
and date+endTime (or parses startTime/endTime as "HH:mm") and ensures startTime
< endTime, returning clear error messages referencing date, startTime, and
endTime when validation fails.
In `@src/modules/availability/availability.model.js`:
- Around line 3-9: Add a compound index to the availability schema to improve
queries that filter by providerId and sort by date and startTime, and enforce
uniqueness to prevent duplicate slots: update availabilitySchema (the
mongoose.Schema instance) to call availabilitySchema.index({ providerId: 1,
date: 1, startTime: 1 }, { unique: true }) so queries that use
providerId/date/startTime are faster and duplicate availability entries are
rejected; ensure the index key order matches how you query/sort in the service.
In `@src/modules/availability/availability.validation.js`:
- Around line 3-8: The current createAvailabilitySchema validates providerId,
date, startTime and endTime but doesn't ensure startTime is before endTime; add
an object-level .refine on createAvailabilitySchema that parses the date plus
startTime/endTime (or parses times into minutes) and returns true only when
start < end, and supply a clear error message (e.g., "Start time must be before
end time") and optionally set the error path to ["startTime"] so the validation
failure surfaces on the startTime field.
In `@src/server.js`:
- Around line 4-6: Replace the direct console.log in the app.listen callback
with the project's logger to keep logs consistent: import the logger utility
(symbol name logger) at the top of the file, then use logger.info (or the
appropriate level) instead of console.log in the app.listen(PORT, ...) callback
and include the same message with PORT; ensure no other console.log remains in
that startup block.
In `@src/shared/config/db.config.js`:
- Around line 3-4: Remove the redundant top-level import of "dotenv/config" in
this module: since MONGODB_URI is imported from env.config.js which already
loads dotenv/config, delete the extra import statement (the standalone
"dotenv/config" import) so only the MONGODB_URI import remains; ensure no other
code relies on the removed side-effect import and run tests/lint to confirm.
- Around line 27-38: Move the mongoose connection event registrations so they
run before attempting to connect: register the listeners using
mongoose.connection.on("connected", ...), .on("error", ...), .on("disconnected",
...), and .on("reconnected", ...) prior to calling mongoose.connect(...) (the
code that currently lives inside the try block). This ensures the handlers
(registered on mongoose.connection) are attached even if mongoose.connect()
fails or reconnects later; after moving them, keep the existing log messages and
leave the mongoose.connect(...) call inside the try/catch as before.
In `@src/shared/config/env.config.js`:
- Line 7: The current zod schema uses transform(Number) for PORT (and similarly
for BCRYPT_ROUNDS and rate limit values) which can produce NaN for invalid
strings; replace transform(Number) with a safe coercion/validation pattern such
as z.preprocess or z.coerce.number() followed by numeric checks (e.g.,
.int().positive().min(1).max(65535) for PORT, and appropriate .int().min/.max
for BCRYPT_ROUNDS and rate limits) or throw a clear error in the transform so
invalid values are rejected instead of silently becoming NaN; update the PORT,
BCRYPT_ROUNDS, and rate limit schema entries to use this validated coercion
pattern.
In `@src/shared/utils/helpers.js`:
- Around line 21-29: The AppError constructor currently calls logger.error(...)
which causes every instantiation to be logged; remove the logging call from the
AppError constructor (the class and its constructor function) so the constructor
only sets message, statusCode, isOperational, timestamp, and capture stack
trace; instead ensure the global error handler middleware (where errors are
formatted and sent) performs logger.error(...) using the AppError instance
details (statusCode, message, timestamp, stack) so logging happens once when the
error is handled.
In `@src/shared/utils/logger.js`:
- Around line 26-43: The redactFormat middleware currently only redacts
top-level keys; update the redactSensitive function used by redactFormat to
recursively traverse objects and arrays and redact any nested keys named
"password", "token", or "authorization" (and variants if desired), including
inside info.metadata and any nested structures; perform the traversal in a safe
way (create/return a cloned object or mutate a shallow copy) to avoid prototype
pollution and ensure all nested occurrences are replaced with "[REDACTED]".
🪄 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: 73fe71f0-8d2c-4d01-b400-07356f8be49a
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (57)
.env.example.gitignoreeslint.config.cjspackage.jsonsrc/modules/admin/admin.controller.jssrc/modules/admin/admin.model.jssrc/modules/admin/admin.routes.jssrc/modules/admin/admin.service.jssrc/modules/admin/admin.validation.jssrc/modules/admin/index.jssrc/modules/appointments/appointment.controller.jssrc/modules/appointments/appointment.model.jssrc/modules/appointments/appointment.routes.jssrc/modules/appointments/appointment.service.jssrc/modules/appointments/appointment.validation.jssrc/modules/appointments/index.jssrc/modules/auth/auth.controller.jssrc/modules/auth/auth.model.jssrc/modules/auth/auth.service.jssrc/modules/auth/auth.validation.jssrc/modules/auth/index.jssrc/modules/availability/availability.controller.jssrc/modules/availability/availability.model.jssrc/modules/availability/availability.routes.jssrc/modules/availability/availability.service.jssrc/modules/availability/availability.validation.jssrc/modules/availability/index.jssrc/modules/providers/index.jssrc/modules/providers/provider.controller.jssrc/modules/providers/provider.model.jssrc/modules/providers/provider.service.jssrc/modules/providers/provider.validation.jssrc/modules/reviews/index.jssrc/modules/reviews/review.controller.jssrc/modules/reviews/review.model.jssrc/modules/reviews/review.routes.jssrc/modules/reviews/review.service.jssrc/modules/reviews/review.validation.jssrc/modules/services/index.jssrc/modules/services/service.controller.jssrc/modules/services/service.model.jssrc/modules/services/service.service.jssrc/modules/services/service.validation.jssrc/modules/users/index.jssrc/modules/users/user.controller.jssrc/modules/users/user.model.jssrc/modules/users/user.service.jssrc/modules/users/user.validation.jssrc/routes/app.jssrc/server.jssrc/shared/config/db.config.jssrc/shared/config/env.config.jssrc/shared/middleware/auth.middleware.jssrc/shared/middleware/error.middleware.jssrc/shared/middleware/index.jssrc/shared/utils/helpers.jssrc/shared/utils/logger.js
| MONGODB_URI= | ||
| MONGODB_URI_PROD= |
There was a problem hiding this comment.
MONGODB_URI_PROD is documented but not validated.
MONGODB_URI_PROD is listed here but isn't defined in env.config.js's schema. Either add it to the Zod schema if it's intended for use, or remove it from the example to avoid confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.env.example around lines 6 - 7, The .env.example lists MONGODB_URI_PROD but
env.config.js's Zod schema does not validate it; either remove MONGODB_URI_PROD
from .env.example or add it to the Zod schema in env.config.js (e.g., add a
MONGODB_URI_PROD entry alongside MONGODB_URI with the same validation rules such
as z.string().nonempty() or an optional z.string() if allowed) and update any
places that read process.env.MONGODB_URI_PROD to use the new schema key.
| "cors": "^2.8.6", | ||
| "dotenv": "^17.3.1", | ||
| "express": "^5.2.1", | ||
| "express-rate-limiter": "^1.3.1", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
npm express-rate-limiter vs express-rate-limit package
💡 Result:
express-rate-limit is the recommended, actively maintained package for rate limiting in Express.js applications. It is a basic IP rate-limiting middleware used to limit repeated requests to public APIs and endpoints like password resets. Latest version: 8.3.1 (published March 2026), with 44.6M weekly downloads, 3.2k GitHub stars, and ongoing updates. Supports configurable windows, limits, stores (in-memory default, Redis/Memcached/etc.), standard rate limit headers, and plays well with related packages like express-slow-down. express-rate-limiter is an older, unmaintained alternative. Latest version: 1.3.1 (published November 2015), with only 1.2K weekly downloads. It provides rate limiting with inner (e.g., hammering protection) and outer limits based on user IP, using a memory store by default (replaceable via store.js interface). No recent activity or significant adoption. Comparison: express-rate-limit vastly outperforms express-rate-limiter in popularity (44M+ vs 1K weekly downloads), maintenance (active in 2026 vs last update 2015), features (modern stores, headers, TypeScript support), and community support. npm trends confirm express-rate-limit dominates while express-rate-limiter has negligible usage. No direct head-to-head comparisons found, but express-rate-limit's official docs position it as the standard, listing alternatives like rate-limiter-flexible for advanced needs. Recommendation: Use express-rate-limit unless specific legacy requirements demand express-rate-limiter. Example usage for express-rate-limit: import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 100 }); app.use(limiter);
Citations:
- 1: https://www.npmjs.com/package/express-rate-limit?activeTab=readme
- 2: https://www.npmjs.com/package/express-rate-limit
- 3: https://express-rate-limit.mintlify.app/overview
- 4: https://www.npmjs.com/package/express-rate-limiter
- 5: https://github.com/express-rate-limit/express-rate-limit
- 6: https://npmtrends.com/express-rate-limit-vs-express-rate-limiter-vs-node-rate-limiter-vs-node-ratelimiter-vs-rate-limiter-flexible
Replace express-rate-limiter with express-rate-limit.
The package express-rate-limiter is unmaintained (last update November 2015) with minimal adoption. The correct package is express-rate-limit, which is actively maintained (latest 8.3.1), has 44.6M weekly downloads, and is the industry standard for Express.js rate limiting. Update the dependency to use express-rate-limit to ensure security updates and modern features.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 30, Replace the unmaintained dependency
"express-rate-limiter" in package.json with the correct maintained package
"express-rate-limit", run your package manager to update lockfiles, and update
any imports/usages that reference "express-rate-limiter" to "express-rate-limit"
(e.g., change require/import strings and ensure the middleware creation uses the
standard rateLimit API). Also search for symbol usages like rateLimiter
middleware instantiation and adjust to the "express-rate-limit" API if needed,
then reinstall and commit updated package.json and lockfile.
| router.post('/', validate(createAppointmentSchema), appointmentController.createAppointment); | ||
|
|
||
| // Get all appointments for a specific user | ||
| router.get('/user/:userId', appointmentController.getUserAppointments); |
There was a problem hiding this comment.
Missing authentication and authorization middleware.
Both routes are unprotected:
POST /allows anyone to create appointments without authenticationGET /user/:userIdallows fetching any user's appointments without verifying the requester's identity or authorization
Additionally, the userId route parameter should be validated as a valid MongoDB ObjectId format before reaching the controller.
🔒 Suggested approach
import { Router } from 'express';
import * as appointmentController from './appointment.controller.js';
import { createAppointmentSchema } from './appointment.validation.js';
import validate from '../../shared/middleware/validate.js'; // optional Zod middleware
+import { authenticate } from '../../shared/middleware/auth.js';
const router = Router();
// Create a new appointment
-router.post('/', validate(createAppointmentSchema), appointmentController.createAppointment);
+router.post('/', authenticate, validate(createAppointmentSchema), appointmentController.createAppointment);
// Get all appointments for a specific user
-router.get('/user/:userId', appointmentController.getUserAppointments);
+router.get('/user/:userId', authenticate, appointmentController.getUserAppointments);
export default router;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/appointments/appointment.routes.js` around lines 9 - 12, Protect
the appointment routes by adding the authentication middleware (e.g., auth or
requireAuth) before controllers so POST '/' uses auth +
validate(createAppointmentSchema) then appointmentController.createAppointment,
and GET '/user/:userId' uses auth and an authorization check that ensures
req.user.id === userId or has admin role before calling
appointmentController.getUserAppointments; also add a param validation
middleware (or use validate with a param schema) to ensure the route param
userId is a valid MongoDB ObjectId (e.g., validateObjectId or Joi/ObjectId
check) before reaching the controller.
| } catch (error) { | ||
| if (error instanceof z.ZodError){ | ||
| logger.error("Invalid Environment Variable "); | ||
| error.errors.forEach(e => logger.error(`-${e.path.join(".")}: ${e.message}`)); | ||
| } | ||
| logger.error("Unkown Environment Validation Error : ", error); | ||
| process.exit(1) | ||
| } |
There was a problem hiding this comment.
Fix error handling control flow — ZodError gets double-logged.
When error instanceof z.ZodError is true, lines 30-31 execute, but then line 33 also runs because there's no return or else. This causes ZodErrors to be logged twice: once with field details and again as "Unknown".
Also, "Unkown" is a typo.
🐛 Proposed fix
const validateEnv = () => {
try {
const parsedEnv = envSchema.parse(process.env);
return parsedEnv
} catch (error) {
if (error instanceof z.ZodError){
logger.error("Invalid Environment Variable ");
error.errors.forEach(e => logger.error(`-${e.path.join(".")}: ${e.message}`));
+ process.exit(1);
+ } else {
+ logger.error("Unknown Environment Validation Error: ", error);
+ process.exit(1);
}
- logger.error("Unkown Environment Validation Error : ", error);
- process.exit(1)
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| if (error instanceof z.ZodError){ | |
| logger.error("Invalid Environment Variable "); | |
| error.errors.forEach(e => logger.error(`-${e.path.join(".")}: ${e.message}`)); | |
| } | |
| logger.error("Unkown Environment Validation Error : ", error); | |
| process.exit(1) | |
| } | |
| } catch (error) { | |
| if (error instanceof z.ZodError){ | |
| logger.error("Invalid Environment Variable "); | |
| error.errors.forEach(e => logger.error(`-${e.path.join(".")}: ${e.message}`)); | |
| process.exit(1); | |
| } else { | |
| logger.error("Unknown Environment Validation Error: ", error); | |
| process.exit(1); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/config/env.config.js` around lines 28 - 35, In the catch block
handling validation errors, avoid double-logging z.ZodError by returning or
using an else after detecting z.ZodError: when (error instanceof z.ZodError) log
the per-field messages via error.errors and then return (or put the generic
logger.error("Unknown Environment Validation Error", error) inside an else) so
it does not run for ZodErrors; also fix the typo "Unkown" to "Unknown" and
ensure process.exit(1) still runs for all error paths.
| } | ||
| } | ||
|
|
||
| // GENERAL ASYNC HANLDER |
There was a problem hiding this comment.
Typo: "HANLDER" → "HANDLER".
-// GENERAL ASYNC HANLDER
+// GENERAL ASYNC HANDLER📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // GENERAL ASYNC HANLDER | |
| // GENERAL ASYNC HANDLER |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/utils/helpers.js` at line 32, Fix the typo in the comment heading
"GENERAL ASYNC HANLDER" by changing "HANLDER" to "HANDLER" so the comment reads
"GENERAL ASYNC HANDLER"; locate the string in src/shared/utils/helpers.js (the
comment line containing "GENERAL ASYNC HANLDER") and update it accordingly.
| }; | ||
|
|
||
|
|
||
| export const generateOTP = () => Math.floor(100000 + Math.random() * 900000).toString(); |
There was a problem hiding this comment.
Math.random() is not cryptographically secure for OTPs.
OTPs used for authentication or verification should use crypto.randomInt() to prevent predictability attacks.
🔒 Proposed fix
+import { randomInt } from 'crypto';
+
-export const generateOTP = () => Math.floor(100000 + Math.random() * 900000).toString();
+export const generateOTP = () => randomInt(100000, 1000000).toString();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const generateOTP = () => Math.floor(100000 + Math.random() * 900000).toString(); | |
| import { randomInt } from 'crypto'; | |
| export const generateOTP = () => randomInt(100000, 1000000).toString(); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/utils/helpers.js` at line 40, The generateOTP function uses
Math.random() which is not cryptographically secure; replace it with Node's
crypto.randomInt to produce a 6-digit OTP. Import the crypto module and change
generateOTP to call crypto.randomInt(100000, 1000000) (upper bound exclusive),
convert the result to a string (add zero-padding if you alter bounds) and return
that value; update any references to generateOTP accordingly.
| export const scheduleReminder = (appointmentDate, callback) => { | ||
| const delay = new Date(appointmentDate) - new Date(); | ||
| if (delay > 0) { | ||
| setTimeout(callback, delay); | ||
| } | ||
| }; |
There was a problem hiding this comment.
setTimeout reminders are lost on server restart.
Scheduled reminders using setTimeout are not persisted. Any server restart, deployment, or crash will lose all pending reminders. For production reliability, consider a job queue (e.g., Bull, Agenda) or database-backed scheduler.
Would you like me to suggest an implementation using a persistent job queue?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/utils/helpers.js` around lines 85 - 90, The scheduleReminder
function currently uses setTimeout (in scheduleReminder) which is lost on server
restarts; replace this ephemeral scheduling with a persistent job queue (e.g.,
Bull or Agenda) and a DB table/collection for reminder records: enqueue a job
instead of calling setTimeout in scheduleReminder(appointmentDate, callback),
persist the reminder payload and job id when creating the job, and on
application startup rehydrate pending reminders by loading unsent records and
scheduling them in the queue (or resuming jobs via the queue's built-in
persistence). Ensure cancel/reschedule flows update both the DB record and the
queued job using the stored job id.
| const colors = { | ||
| error: "red", | ||
| warn: "yellow", | ||
| info: "green", | ||
| http: "magenta", | ||
| debug: "purple", | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
winston addColors valid color names chalk
💡 Result:
Winston's addColors accepts color names from the @colors/colors library (used by winston/logform colorize format), which are standard ANSI terminal colors and modifiers. These are the valid color names: Basic foreground colors: black, red, green, yellow, blue, magenta, cyan, white, gray, grey Bright foreground colors: brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite Background colors: blackBG, redBG, greenBG, yellowBG, blueBG, magentaBG, cyanBG, whiteBG Style modifiers (combinable): bold, dim, italic, underline, inverse, hidden, strikethrough Examples from sources: - winston.addColors({ error: 'red', warn: 'yellow', info: 'green', debug: 'blue' }); - winston.addColors({ error: 'brightRed', warn: 'brightYellow', info: 'brightBlue', debug: 'brightGreen' }); - winston.addColors({ debug: 'rainbow', info: 'cyan', notice: 'white', warning: 'yellow', error: 'bold red', crit: 'inverse yellow' }); Invalid colors like 'brown', 'pink', or hex codes (#2E6E3E) cause errors because they are not defined in the colors library. Winston's colorize does NOT use Chalk directly (Chalk is a separate library for more advanced styling; some users combine it manually with printf format). Colors are powered by @colors/colors v1.6.0 (as of latest logform updates). To use: Call winston.addColors({ level: 'colorName' }) before creating logger with colorize format. Hex/custom colors require manual ANSI or Chalk integration.
Citations:
- 1: https://examplejavascript.com/winston/addcolors/
- 2: https://stackoverflow.com/questions/69938287/what-are-the-allowed-colors-in-winston-3
- 3: Console logging is not colorized. winstonjs/winston#1135
- 4: https://stackoverflow.com/questions/47133451/can-i-change-the-color-of-log-data-in-winston
- 5: https://stackoverflow.com/questions/51012150/winston-3-0-colorize-whole-output-on-console
- 6: Colourized timestamp winstonjs/logform#112
- 7: https://www.npmjs.com/package/winston-timestamp-colorize
- 8: https://github.com/winstonjs/logform
- 9: Bump @colors/colors from 1.5.0 to 1.6.0 winstonjs/logform#278
- 10: https://registry.npmjs.org/logform
- 11: https://github.com/winstonjs/logform/blob/master/index.js
- 12: https://stackoverflow.com/questions/60149797/how-to-color-winston-logger
- 13: https://stackoverflow.com/questions/51012150/winston-3-0-colorize-whole-output-on-console/64173978
Replace "purple" with a valid Winston color.
The color "purple" is not supported by Winston's color system. Valid colors include: black, red, green, yellow, blue, magenta, cyan, white, gray. Use magenta, blue, or brightMagenta for the debug level instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/utils/logger.js` around lines 16 - 22, The colors mapping uses an
unsupported value "purple" for the debug level; update the colors object (the
debug property in the colors constant) to a valid Winston color such as "blue"
(or "magenta"/"brightMagenta") so Winston's colorizer accepts it—locate the
colors constant and replace the debug: "purple" entry with debug: "blue".
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/modules/appointments/appointment.controller.js (1)
7-18: Error message matching is fragile; consider custom error classes.The current approach relies on exact string matching of error messages from the service. If those messages change, client errors will incorrectly fall through to the 500 path. Additionally, line 12 could throw a
TypeErrorif a non-standard error with undefinedmessageis thrown.♻️ Suggested approach using custom error class
Create a custom error class in a shared module:
// src/utils/errors.js export class ClientError extends Error { constructor(message) { super(message); this.name = 'ClientError'; this.isClientError = true; } }Update the service to throw
ClientError:import { ClientError } from '../../utils/errors.js'; // ... if (!slot) throw new ClientError('Slot is not available');Simplify the controller check:
} catch (err) { - if (err.name === 'ValidationError' || - err.message === 'Slot is not available' || - err.message === 'This slot is already booked' || - err.message.includes('Invalid date')) { + if (err.name === 'ValidationError' || err.isClientError) { res.status(400).json({ message: err.message }); } else { next(err); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/appointments/appointment.controller.js` around lines 7 - 18, The catch block in the appointment controller relies on fragile string matching of err.message and may throw if message is undefined; create a custom ClientError class (e.g., class ClientError extends Error { constructor(m){ super(m); this.name='ClientError'; this.isClientError=true } }) in a shared utils/errors module, update the service code that currently throws strings or ValidationError to throw new ClientError(...) for client-caused failures (e.g., "Slot is not available"), and simplify the controller catch to treat errors as client errors when err instanceof ClientError or err.isClientError is true (use optional chaining like err?.message when reading the message) and fall back to next(err) for all other errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/modules/appointments/appointment.controller.js`:
- Around line 7-18: The catch block in the appointment controller relies on
fragile string matching of err.message and may throw if message is undefined;
create a custom ClientError class (e.g., class ClientError extends Error {
constructor(m){ super(m); this.name='ClientError'; this.isClientError=true } })
in a shared utils/errors module, update the service code that currently throws
strings or ValidationError to throw new ClientError(...) for client-caused
failures (e.g., "Slot is not available"), and simplify the controller catch to
treat errors as client errors when err instanceof ClientError or
err.isClientError is true (use optional chaining like err?.message when reading
the message) and fall back to next(err) for all other errors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 044bd9db-d95c-496c-a07f-f4db170eaf30
📒 Files selected for processing (6)
src/modules/appointments/appointment.controller.jssrc/modules/appointments/appointment.model.jssrc/modules/appointments/appointment.routes.jssrc/modules/appointments/appointment.service.jssrc/modules/appointments/appointment.validation.jssrc/modules/appointments/index.js
🚧 Files skipped from review as they are similar to previous changes (5)
- src/modules/appointments/appointment.routes.js
- src/modules/appointments/appointment.model.js
- src/modules/appointments/appointment.service.js
- src/modules/appointments/index.js
- src/modules/appointments/appointment.validation.js
Adds fully structured appointments and availability modules with models, services, controllers, validations, and routes for the backend API. Ready to integrate into app.js.
Summary by CodeRabbit
New Features
Chores