diff --git a/backend/package.json b/backend/package.json index 505a78f..4bfbaa8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,6 +3,7 @@ "type": "module", "scripts": { "start:dev": "tsx watch --env-file=.env src/index.ts", + "start:prod": "tsx watch src/index.ts", "lint": "eslint .", "lint:fix": "eslint . --fix" }, diff --git a/backend/src/controllers/notes/get-specific-note.controller.ts b/backend/src/controllers/notes/get-specific-note.controller.ts new file mode 100644 index 0000000..f22d398 --- /dev/null +++ b/backend/src/controllers/notes/get-specific-note.controller.ts @@ -0,0 +1,36 @@ +import type { Context } from "hono"; + +import { StatusCodes } from "http-status-codes"; +import { Types } from "mongoose"; + +import Note from "@/models/notes.model.js"; +import { responseHandler } from "@/utils/response.js"; + +export default async (c: Context) => { + const noteId = c.req.param("id"); + const { userId } = c.get("user"); + + // Validate ObjectId format + if (!Types.ObjectId.isValid(noteId) || !Types.ObjectId.isValid(userId)) { + return c.json(responseHandler(false, "Invalid note ID or user ID"), StatusCodes.BAD_REQUEST); + } + + // Convert to ObjectId + const noteObjectId = new Types.ObjectId(noteId); + const userObjectId = new Types.ObjectId(userId); + + // Fetch the note ensuring it belongs to the authenticated user + const note = await Note.findOne({ + _id: noteObjectId, + userId: userObjectId, + }); + + if (!note) + return c.json(responseHandler(false, "Note not found or unauthorized access"), StatusCodes.NOT_FOUND); + + return c.json(responseHandler(true, `Fetched the note with Id:${note}`, { + noteId, + userId, + note: note.toObject(), + }), StatusCodes.OK); +}; diff --git a/backend/src/models/notes.model.ts b/backend/src/models/notes.model.ts index a0df8fa..23ee8a5 100644 --- a/backend/src/models/notes.model.ts +++ b/backend/src/models/notes.model.ts @@ -36,8 +36,15 @@ const NoteSchema = new Schema( } ); +<<<<<<< Updated upstream +<<<<<<< Updated upstream // 🔥 Indexing for Performance // Compound index for retrieving notes by user & sorting by creation time +======= +======= +>>>>>>> Stashed changes +// Indexing for Performance +>>>>>>> Stashed changes NoteSchema.index({ userId: 1, createdAt: -1 }); // Index tags as multi-key field (each tag is indexed for efficient querying) NoteSchema.index({ tags: 1 }); diff --git a/backend/src/routes/notes.route.ts b/backend/src/routes/notes.route.ts index 0d234af..73138ad 100644 --- a/backend/src/routes/notes.route.ts +++ b/backend/src/routes/notes.route.ts @@ -2,6 +2,7 @@ import { Hono } from "hono"; import createNotesController from "@/controllers/notes/create-notes.controller.js"; import getNotesController from "@/controllers/notes/get-notes.controller.js"; +import getSpecificNoteController from "@/controllers/notes/get-specific-note.controller.js"; import updateNotesController from "@/controllers/notes/update-notes.controller.js"; import authMiddleware from "@/middlewares/auth.middleware.js"; import { @@ -18,6 +19,7 @@ const useRouter = new Hono<{ Variables: { user: { userId: string } } }>(); useRouter.use(authMiddleware); useRouter.get("/", validateRequestDto(undefined, getNotesQuerySchema), getNotesController); +useRouter.get("/:id", validateRequestDto(undefined, undefined, NotesParamsSchema), getSpecificNoteController); useRouter.post("/", validateRequestDto(createNoteSchema), createNotesController); useRouter.patch("/:id", validateRequestDto(updateNoteSchema, undefined, NotesParamsSchema), updateNotesController); useRouter.delete("/:id", validateRequestDto(undefined, undefined, NotesParamsSchema), deleteNotesController) diff --git a/backend/src/validations/schemas/notes.schema.ts b/backend/src/validations/schemas/notes.schema.ts index 1e7c0d6..54f16c0 100644 --- a/backend/src/validations/schemas/notes.schema.ts +++ b/backend/src/validations/schemas/notes.schema.ts @@ -34,18 +34,39 @@ export const getNotesQuerySchema = z.object({ // Zod schema for creating new note export const createNoteSchema = z.object({ +<<<<<<< Updated upstream title: z.string().min(1, "Title is required").max(255), content: z.string().min(1, "Content is required"), images: z.array(z.string().url()).optional().default([]), favorite: z.boolean().optional().default(false), pinned: z.boolean().optional().default(false), tags: z.array(z.string()).optional().default([]), // Must be an array of strings +======= + title: z.string() + .min(1, "Title is required") + .max(255) + .trim(), + // content: z.array(z.any()), + content: z.string().min(1, "Content can't be empty"), + images: z.array(z.string().url()).optional().default([]), // Must be valid URLs + favorite: z.boolean().optional().default(false), // Defaults to false + pinned: z.boolean().optional().default(false), // Defaults to false + tags: z.array(z.string().trim().min(1)).max(10).optional().default([]), +>>>>>>> Stashed changes }); // Zod schema for note update export const updateNoteSchema = z.object({ // noteId: z.string({ message: "NoteId is required for updating it" }).min(1, "NoteId is required for updating it"), title: z.string().min(1, "Title is required").max(255).optional(), +<<<<<<< Updated upstream +<<<<<<< Updated upstream +======= + // content: z.array(z.any()).optional(), +>>>>>>> Stashed changes +======= + // content: z.array(z.any()).optional(), +>>>>>>> Stashed changes content: z.string().optional(), images: z.array(z.string().url()).optional(), favorite: z.boolean().optional(),