Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/api/comments/public/public.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getPaginationLimit } from '@/utils/pagination'
import { getSearchParams } from '@/utils/request'
import { decode, encode } from 'js-base64'
import { NextRequest, NextResponse } from 'next/server'
import { ValidateUuid } from '@api/core/utils/validateUuid'
import { PublicResource } from '@api/core/types/public'

type TaskAndCommentIdParams = {
params: Promise<{ id: string }>
Expand Down Expand Up @@ -53,6 +55,7 @@ export const getAllCommentsPublic = async (req: NextRequest) => {

export const getOneCommentPublic = async (req: NextRequest, { params }: TaskAndCommentIdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Comments)
const user = await authenticate(req)

const commentService = new CommentService(user)
Expand All @@ -66,6 +69,7 @@ export const getOneCommentPublic = async (req: NextRequest, { params }: TaskAndC

export const deleteOneCommentPublic = async (req: NextRequest, { params }: TaskAndCommentIdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Comments)
const user = await authenticate(req)

const commentService = new CommentService(user)
Expand Down
5 changes: 5 additions & 0 deletions src/app/api/core/types/public.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum PublicResource {
Tasks = 'task',
Templates = 'template',
Comments = 'comment',
}
24 changes: 24 additions & 0 deletions src/app/api/core/utils/validateUuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import httpStatus from 'http-status'
import z from 'zod'
import APIError from '@api/core/exceptions/api'
import { PublicResource } from '@api/core/types/public'

/**
* Validates that an ID parameter is a valid UUID.
*
* This utility is intended to be used in service-layer retrieval flows
* where an ID is passed via API params. If the ID is not a valid UUID,
* it throws a 404 error immediately instead of letting the request hit Prisma,
* thereby avoiding Prisma P2023 errors and providing a controlled,
* consistent API response. Mainly used for public APIs.
*
* @param id - The resource ID to validate
* @param resourceType - The type of resource being retrieved
*
* @throws {APIError} 404 NOT_FOUND if the ID is not a valid UUID
*/
export const ValidateUuid = (id: string, resourceType: PublicResource) => {
if (!z.string().uuid().safeParse(id).success) {
throw new APIError(httpStatus.NOT_FOUND, `The requested ${resourceType} was not found`)
}
}
5 changes: 5 additions & 0 deletions src/app/api/tasks/public/public.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { decode, encode } from 'js-base64'
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { PublicTasksService } from '@api/tasks/public/public.service'
import { ValidateUuid } from '@api/core/utils/validateUuid'
import { PublicResource } from '@api/core/types/public'

export const getAllTasksPublic = async (req: NextRequest) => {
const user = await authenticate(req)
Expand Down Expand Up @@ -54,6 +56,7 @@ export const getAllTasksPublic = async (req: NextRequest) => {

export const getOneTaskPublic = async (req: NextRequest, { params }: IdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Tasks)
const user = await authenticate(req)
const tasksService = new PublicTasksService(user)
const task = await tasksService.getOneTask(id)
Expand All @@ -78,6 +81,7 @@ export const createTaskPublic = async (req: NextRequest) => {

export const updateTaskPublic = async (req: NextRequest, { params }: IdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Tasks)
const user = await authenticate(req)
const data = PublicTaskUpdateDtoSchema.parse(await req.json())

Expand All @@ -90,6 +94,7 @@ export const updateTaskPublic = async (req: NextRequest, { params }: IdParams) =

export const deleteOneTaskPublic = async (req: NextRequest, { params }: IdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Tasks)
const recursive = req.nextUrl.searchParams.get('recursive')
const user = await authenticate(req)
const tasksService = new PublicTasksService(user)
Expand Down
3 changes: 3 additions & 0 deletions src/app/api/tasks/templates/public/public.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { TemplatesService } from '@/app/api/tasks/templates/templates.service'
import { defaultLimit } from '@/constants/public-api'
import { getSearchParams } from '@/utils/request'
import { encode, decode } from 'js-base64'
import { ValidateUuid } from '@api/core/utils/validateUuid'
import { PublicResource } from '@api/core/types/public'

import { NextRequest, NextResponse } from 'next/server'

Expand All @@ -24,6 +26,7 @@ export const getTaskTemplatesPublic = async (req: NextRequest) => {

export const getTaskTemplatePublic = async (req: NextRequest, { params }: IdParams) => {
const { id } = await params
ValidateUuid(id, PublicResource.Tasks)
const user = await authenticate(req)
const templatesService = new TemplatesService(user)
const template = await templatesService.getOneTemplate(id)
Expand Down
Loading