[
  {
    "path": ".gitignore",
    "content": "node_modules\n# Keep environment variables out of version control\n.env\ndist"
  },
  {
    "path": "README.md",
    "content": "# pass.in\n\nO pass.in é uma aplicação de **gestão de participantes em eventos presenciais**. \n\nA ferramenta permite que o organizador cadastre um evento e abra uma página pública de inscrição.\n\nOs participantes inscritos podem emitir uma credencial para check-in no dia do evento.\n\nO sistema fará um scan da credencial do participante para permitir a entrada no evento.\n\n## Requisitos\n\n### Requisitos funcionais\n\n- [x] O organizador deve poder cadastrar um novo evento;\n- [x] O organizador deve poder visualizar dados de um evento;\n- [x] O organizador deve poser visualizar a lista de participantes; \n- [x] O participante deve poder se inscrever em um evento;\n- [x] O participante deve poder visualizar seu crachá de inscrição;\n- [x] O participante deve poder realizar check-in no evento;\n\n### Regras de negócio\n\n- [x] O participante só pode se inscrever em um evento uma única vez;\n- [x] O participante só pode se inscrever em eventos com vagas disponíveis;\n- [x] O participante só pode realizar check-in em um evento uma única vez;\n\n### Requisitos não-funcionais\n\n- [x] O check-in no evento será realizado através de um QRCode;\n\n## Documentação da API (Swagger)\n\nPara documentação da API, acesse o link: https://nlw-unite-nodejs.onrender.com/docs\n\n## Banco de dados\n\nNessa aplicação vamos utilizar banco de dados relacional (SQL). Para ambiente de desenvolvimento seguiremos com o SQLite pela facilidade do ambiente.\n\n### Diagrama ERD\n\n<img src=\".github/erd.svg\" width=\"600\" alt=\"Diagrama ERD do banco de dados\" />\n\n### Estrutura do banco (SQL)\n\n```sql\n-- CreateTable\nCREATE TABLE \"events\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"title\" TEXT NOT NULL,\n    \"details\" TEXT,\n    \"slug\" TEXT NOT NULL,\n    \"maximum_attendees\" INTEGER\n);\n\n-- CreateTable\nCREATE TABLE \"attendees\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"name\" TEXT NOT NULL,\n    \"email\" TEXT NOT NULL,\n    \"event_id\" TEXT NOT NULL,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    CONSTRAINT \"attendees_event_id_fkey\" FOREIGN KEY (\"event_id\") REFERENCES \"events\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateTable\nCREATE TABLE \"check_ins\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"attendeeId\" INTEGER NOT NULL,\n    CONSTRAINT \"check_ins_attendeeId_fkey\" FOREIGN KEY (\"attendeeId\") REFERENCES \"attendees\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"events_slug_key\" ON \"events\"(\"slug\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"attendees_event_id_email_key\" ON \"attendees\"(\"event_id\", \"email\");\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"check_ins_attendeeId_key\" ON \"check_ins\"(\"attendeeId\");\n```"
  },
  {
    "path": "api.http",
    "content": "POST http://localhost:3333/events\nContent-Type: application/json\n\n{\n  \"title\": 123,\n  \"details\": null,\n  \"maximumAttendees\": 1\n}\n\n###\n\nPOST http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43/attendees\nContent-Type: application/json\n\n{\n  \"name\": \"Diego Fernandes\",\n  \"email\": \"diego2@rocketseat.com.br\"\n}\n\n###\n\nGET http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43\n\n###\n\nGET http://localhost:3333/attendees/3/badge\n###\n\nGET http://localhost:3333/attendees/4/check-in\n\n###\n\nGET http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43/attendees?query=diego"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"server-node\",\n  \"version\": \"1.0.0\",\n  \"description\": \"O pass.in é uma aplicação de **gestão de participantes em eventos presenciais**.\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"start\": \"node dist/server.mjs\",\n    \"build\": \"tsup src --format esm\",\n    \"dev\": \"tsx watch --env-file .env src/server.ts\",\n    \"db:migrate\": \"prisma migrate dev\",\n    \"db:studio\": \"prisma studio\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"prisma\": {\n    \"seed\": \"tsx prisma/seed.ts\"\n  },\n  \"devDependencies\": {\n    \"@faker-js/faker\": \"8.4.1\",\n    \"@types/node\": \"20.11.30\",\n    \"prisma\": \"5.11.0\",\n    \"tsup\": \"8.0.2\",\n    \"tsx\": \"4.7.1\",\n    \"typescript\": \"5.4.3\"\n  },\n  \"dependencies\": {\n    \"@fastify/cors\": \"9.0.1\",\n    \"@fastify/swagger\": \"8.14.0\",\n    \"@fastify/swagger-ui\": \"3.0.0\",\n    \"@prisma/client\": \"5.11.0\",\n    \"dayjs\": \"1.11.10\",\n    \"fastify\": \"4.26.2\",\n    \"fastify-type-provider-zod\": \"1.1.9\",\n    \"zod\": \"3.22.4\"\n  }\n}\n"
  },
  {
    "path": "prisma/migrations/20240325193227_create_table_events/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE \"events\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"title\" TEXT NOT NULL,\n    \"details\" TEXT,\n    \"slug\" TEXT NOT NULL,\n    \"maximum_attendees\" INTEGER\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"events_slug_key\" ON \"events\"(\"slug\");\n"
  },
  {
    "path": "prisma/migrations/20240326182647_create_table_attendees/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE \"attendees\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"name\" TEXT NOT NULL,\n    \"email\" TEXT NOT NULL,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"event_id\" TEXT NOT NULL,\n    CONSTRAINT \"attendees_event_id_fkey\" FOREIGN KEY (\"event_id\") REFERENCES \"events\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n"
  },
  {
    "path": "prisma/migrations/20240326184908_add_uniqueness_on_event_id_and_email/migration.sql",
    "content": "/*\n  Warnings:\n\n  - A unique constraint covering the columns `[event_id,email]` on the table `attendees` will be added. If there are existing duplicate values, this will fail.\n\n*/\n-- CreateIndex\nCREATE UNIQUE INDEX \"attendees_event_id_email_key\" ON \"attendees\"(\"event_id\", \"email\");\n"
  },
  {
    "path": "prisma/migrations/20240327140503_create_check_ins_table/migration.sql",
    "content": "-- CreateTable\nCREATE TABLE \"check_ins\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"attendee_id\" INTEGER NOT NULL,\n    CONSTRAINT \"check_ins_attendee_id_fkey\" FOREIGN KEY (\"attendee_id\") REFERENCES \"attendees\" (\"id\") ON DELETE RESTRICT ON UPDATE CASCADE\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"check_ins_attendee_id_key\" ON \"check_ins\"(\"attendee_id\");\n"
  },
  {
    "path": "prisma/migrations/20240327143815_add_cascades/migration.sql",
    "content": "-- RedefineTables\nPRAGMA foreign_keys=OFF;\nCREATE TABLE \"new_check_ins\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"attendee_id\" INTEGER NOT NULL,\n    CONSTRAINT \"check_ins_attendee_id_fkey\" FOREIGN KEY (\"attendee_id\") REFERENCES \"attendees\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\nINSERT INTO \"new_check_ins\" (\"attendee_id\", \"created_at\", \"id\") SELECT \"attendee_id\", \"created_at\", \"id\" FROM \"check_ins\";\nDROP TABLE \"check_ins\";\nALTER TABLE \"new_check_ins\" RENAME TO \"check_ins\";\nCREATE UNIQUE INDEX \"check_ins_attendee_id_key\" ON \"check_ins\"(\"attendee_id\");\nCREATE TABLE \"new_attendees\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"name\" TEXT NOT NULL,\n    \"email\" TEXT NOT NULL,\n    \"created_at\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"event_id\" TEXT NOT NULL,\n    CONSTRAINT \"attendees_event_id_fkey\" FOREIGN KEY (\"event_id\") REFERENCES \"events\" (\"id\") ON DELETE CASCADE ON UPDATE CASCADE\n);\nINSERT INTO \"new_attendees\" (\"created_at\", \"email\", \"event_id\", \"id\", \"name\") SELECT \"created_at\", \"email\", \"event_id\", \"id\", \"name\" FROM \"attendees\";\nDROP TABLE \"attendees\";\nALTER TABLE \"new_attendees\" RENAME TO \"attendees\";\nCREATE UNIQUE INDEX \"attendees_event_id_email_key\" ON \"attendees\"(\"event_id\", \"email\");\nPRAGMA foreign_key_check;\nPRAGMA foreign_keys=ON;\n"
  },
  {
    "path": "prisma/migrations/migration_lock.toml",
    "content": "# Please do not edit this file manually\n# It should be added in your version-control system (i.e. Git)\nprovider = \"sqlite\""
  },
  {
    "path": "prisma/schema.prisma",
    "content": "generator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel Event {\n  id               String  @id @default(uuid())\n  title            String\n  details          String?\n  slug             String  @unique\n  maximumAttendees Int?    @map(\"maximum_attendees\")\n\n  attendees Attendee[]\n\n  @@map(\"events\")\n}\n\nmodel Attendee {\n  id        Int      @id @default(autoincrement())\n  name      String\n  email     String\n  createdAt DateTime @default(now()) @map(\"created_at\")\n  eventId   String   @map(\"event_id\")\n\n  event   Event    @relation(fields: [eventId], references: [id], onDelete: Cascade)\n  checkIn CheckIn?\n\n  @@unique([eventId, email])\n  @@map(\"attendees\")\n}\n\nmodel CheckIn {\n  id        Int      @id @default(autoincrement())\n  createdAt DateTime @default(now()) @map(\"created_at\")\n\n  attendee   Attendee @relation(fields: [attendeeId], references: [id], onDelete: Cascade)\n  attendeeId Int      @unique @map(\"attendee_id\")\n\n  @@map(\"check_ins\")\n}\n"
  },
  {
    "path": "prisma/seed.ts",
    "content": "import { prisma } from '../src/lib/prisma'\nimport { faker } from '@faker-js/faker'\nimport { Prisma } from '@prisma/client'\nimport dayjs from 'dayjs'\n\nasync function seed() {\n  const eventId = '9e9bd979-9d10-4915-b339-3786b1634f33'\n\n  await prisma.event.deleteMany()\n\n  await prisma.event.create({\n    data: {\n      id: eventId,\n      title: 'Unite Summit',\n      slug: 'unite-summit',\n      details: 'Um evento p/ devs apaixonados(as) por código!',\n      maximumAttendees: 120,\n    }\n  })\n\n  const attendeesToInsert: Prisma.AttendeeUncheckedCreateInput[] = []\n\n  for (let i = 0; i <= 120; i++) {\n    attendeesToInsert.push({\n      id: 10000 + i,\n      name: faker.person.fullName(),\n      email: faker.internet.email(),\n      eventId,\n      createdAt: faker.date.recent({ days: 30, refDate: dayjs().subtract(8, \"days\").toDate() }),\n      checkIn: faker.helpers.arrayElement<Prisma.CheckInUncheckedCreateNestedOneWithoutAttendeeInput | undefined>([\n        undefined,\n        {\n          create: {\n            createdAt: faker.date.recent({ days: 7 }),\n          }\n        }\n      ])\n    })\n  }\n\n  await Promise.all(attendeesToInsert.map(data => {\n    return prisma.attendee.create({\n      data,\n    })\n  }))\n}\n\nseed().then(() => {\n  console.log('Database seeded!')\n  prisma.$disconnect()\n})"
  },
  {
    "path": "src/error-handler.ts",
    "content": "import { FastifyInstance } from 'fastify'\nimport { BadRequest } from './routes/_errors/bad-request'\nimport { ZodError } from 'zod'\n\ntype FastifyErrorHandler = FastifyInstance['errorHandler']\n\nexport const errorHandler: FastifyErrorHandler = (error, request, reply) => {\n  if (error instanceof ZodError) {\n    return reply.status(400).send({\n      message: 'Error during validation',\n      errors: error.flatten().fieldErrors,\n    })\n  }\n\n  if (error instanceof BadRequest) {\n    return reply.status(400).send({ message: error.message })\n  }\n\n  return reply.status(500).send({ message: 'Internal server error!' })\n}"
  },
  {
    "path": "src/lib/prisma.ts",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\nexport const prisma = new PrismaClient({\n  log: ['query'],\n})"
  },
  {
    "path": "src/routes/_errors/bad-request.ts",
    "content": "export class BadRequest extends Error {}"
  },
  {
    "path": "src/routes/check-in.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport z from \"zod\";\nimport { prisma } from \"../lib/prisma\";\nimport { BadRequest } from \"./_errors/bad-request\";\n\nexport async function checkIn(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .get('/attendees/:attendeeId/check-in', {\n      schema: {\n        summary: 'Check-in an attendee',\n        tags: ['check-ins'],\n        params: z.object({\n          attendeeId: z.coerce.number().int()\n        }),\n        response: {\n          201: z.null(),\n        }\n      }\n    }, async (request, reply) => {\n      const { attendeeId } = request.params\n\n      const attendeeCheckIn = await prisma.checkIn.findUnique({\n        where: {\n          attendeeId,\n        }\n      })\n\n      if (attendeeCheckIn !== null) {\n        throw new BadRequest('Attendee already checked in!')\n      }\n\n      await prisma.checkIn.create({\n        data: {\n          attendeeId,\n        }\n      })\n\n      return reply.status(201).send()\n    })\n}"
  },
  {
    "path": "src/routes/create-event.ts",
    "content": "import { ZodTypeProvider } from \"fastify-type-provider-zod\"\nimport { z } from \"zod\"\nimport { generateSlug } from \"../utils/generate-slug\"\nimport { prisma } from \"../lib/prisma\"\nimport { FastifyInstance } from \"fastify\"\nimport { BadRequest } from \"./_errors/bad-request\"\n\nexport async function createEvent(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .post('/events', {\n      schema: {\n        summary: 'Create an event',\n        tags: ['events'],\n        body: z.object({\n          title: z.string().min(4),\n          details: z.string().nullable(),\n          maximumAttendees: z.number().int().positive().nullable(),\n        }),\n        response: {\n          201: z.object({\n            eventId: z.string().uuid(),\n          })\n        },\n      },\n    }, async (request, reply) => {\n      const {\n        title,\n        details,\n        maximumAttendees,\n      } = request.body\n\n      const slug = generateSlug(title)\n\n      const eventWithSameSlug = await prisma.event.findUnique({\n        where: {\n          slug,\n        }\n      })\n\n      if (eventWithSameSlug !== null) {\n        throw new BadRequest('Another event with same title already exists.')\n      }\n\n      const event = await prisma.event.create({\n        data: {\n          title,\n          details,\n          maximumAttendees,\n          slug,\n        },\n      })\n\n      return reply.status(201).send({ eventId: event.id })\n    })\n}\n\n"
  },
  {
    "path": "src/routes/get-attendee-badge.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } from \"zod\";\nimport { prisma } from \"../lib/prisma\";\nimport { BadRequest } from \"./_errors/bad-request\";\n\nexport async function getAttendeeBadge(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .get('/attendees/:attendeeId/badge', {\n      schema: {\n        summary: 'Get an attendee badge',\n        tags: ['attendees'],\n        params: z.object({\n          attendeeId: z.coerce.number().int(),\n        }),\n        response: {\n          200: z.object({\n            badge: z.object({\n              name: z.string(),\n              email: z.string().email(),\n              eventTitle: z.string(),\n              checkInURL: z.string().url(),\n            })\n          })\n        },\n      }\n    }, async (request, reply) => {\n      const { attendeeId } = request.params\n\n      const attendee = await prisma.attendee.findUnique({\n        select: {\n          name: true,\n          email: true,\n          event: {\n            select: {\n              title: true,\n            },\n          },\n        },\n        where: {\n          id: attendeeId,\n        }\n      })\n\n      if (attendee === null) {\n        throw new BadRequest('Attendee not found.')\n      }\n\n      const baseURL = `${request.protocol}://${request.hostname}`\n\n      const checkInURL = new URL(`/attendees/${attendeeId}/check-in`, baseURL)\n\n      return reply.send({\n        badge: {\n          name: attendee.name,\n          email: attendee.email,\n          eventTitle: attendee.event.title,\n          checkInURL: checkInURL.toString(),\n        }\n      })\n    })\n}"
  },
  {
    "path": "src/routes/get-event-attendees.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } from \"zod\";\nimport { prisma } from \"../lib/prisma\";\n\nexport async function getEventAttendees(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .get('/events/:eventId/attendees', {\n      schema: {\n        summary: 'Get event attendees',\n        tags: ['events'],\n        params: z.object({\n          eventId: z.string().uuid(),\n        }),\n        querystring: z.object({\n          query: z.string().nullish(),\n          pageIndex: z.string().nullish().default('0').transform(Number),\n        }),\n        response: {\n          200: z.object({\n            attendees: z.array(\n              z.object({\n                id: z.number(),\n                name: z.string(),\n                email: z.string().email(),\n                createdAt: z.date(),\n                checkedInAt: z.date().nullable(),\n              })\n            ),\n            total: z.number(),\n          }),\n        },\n      }\n    }, async (request, reply) => {\n      const { eventId } = request.params\n      const { pageIndex, query } = request.query\n\n      const [attendees, total] = await Promise.all([\n        prisma.attendee.findMany({\n          select: {\n            id: true,\n            name: true,\n            email: true,\n            createdAt: true,\n            checkIn: {\n              select: {\n                createdAt: true,\n              }\n            }\n          },\n          where: query ? {\n            eventId,\n            name: {\n              contains: query,\n            }\n          } : {\n            eventId,\n          },\n          take: 10,\n          skip: pageIndex * 10,\n          orderBy: {\n            createdAt: 'desc'\n          }\n        }),\n        prisma.attendee.count({\n          where: query ? {\n            eventId,\n            name: {\n              contains: query,\n            }\n          } : {\n            eventId,\n          },\n        })\n      ])\n\n      return reply.send({ \n        attendees: attendees.map(attendee => {\n          return {\n            id: attendee.id,\n            name: attendee.name,\n            email: attendee.email,\n            createdAt: attendee.createdAt,\n            checkedInAt: attendee.checkIn?.createdAt ?? null,\n          }\n        }),\n        total,\n      })\n    })\n}"
  },
  {
    "path": "src/routes/get-event.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } from \"zod\";\nimport { prisma } from \"../lib/prisma\";\nimport { BadRequest } from \"./_errors/bad-request\";\n\nexport async function getEvent(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .get('/events/:eventId', {\n      schema: {\n        summary: 'Get an event',\n        tags: ['events'],\n        params: z.object({\n          eventId: z.string().uuid(),\n        }),\n        response: {\n          200: z.object({\n            event: z.object({\n              id: z.string().uuid(),\n              title: z.string(),\n              slug: z.string(),\n              details: z.string().nullable(),\n              maximumAttendees: z.number().int().nullable(),\n              attendeesAmount: z.number().int(),\n            })\n          }),\n        },\n      }\n    }, async (request, reply) => {\n      const { eventId } = request.params\n\n      const event = await prisma.event.findUnique({\n        select: {\n          id: true,\n          title: true,\n          slug: true,\n          details: true,\n          maximumAttendees: true,\n          _count: {\n            select: {\n              attendees: true,\n            }\n          },\n        },\n        where: {\n          id: eventId,\n        }\n      })\n\n      if (event === null) {\n        throw new BadRequest('Event not found.')\n      }\n\n      return reply.send({ \n        event: {\n          id: event.id,\n          title: event.title,\n          slug: event.slug,\n          details: event.details,\n          maximumAttendees: event.maximumAttendees,\n          attendeesAmount: event._count.attendees,\n        },\n      })\n    })\n}"
  },
  {
    "path": "src/routes/register-for-event.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } from \"zod\";\nimport { prisma } from \"../lib/prisma\";\nimport { BadRequest } from \"./_errors/bad-request\";\n\nexport async function registerForEvent(app: FastifyInstance) {\n  app\n    .withTypeProvider<ZodTypeProvider>()\n    .post('/events/:eventId/attendees', {\n      schema: {\n        summary: 'Register an attendee',\n        tags: ['attendees'],\n        body: z.object({\n          name: z.string().min(4),\n          email: z.string().email(),\n        }),\n        params: z.object({\n          eventId: z.string().uuid(),\n        }),\n        response: {\n          201: z.object({\n            attendeeId: z.number(),\n          })\n        }\n      }\n    }, async (request, reply) => {\n      const { eventId } = request.params\n      const { name, email } = request.body\n\n      const attendeeFromEmail = await prisma.attendee.findUnique({\n        where: {\n          eventId_email: {\n            email,\n            eventId\n          }\n        }\n      })\n\n      if (attendeeFromEmail !== null) {\n        throw new BadRequest('This e-mail is already registered for this event.')\n      }\n\n      const [event, amountOfAttendeesForEvent] = await Promise.all([\n        prisma.event.findUnique({\n          where: {\n            id: eventId,\n          }\n        }),\n\n        prisma.attendee.count({\n          where: {\n            eventId,\n          }\n        })\n      ])\n\n      if (event?.maximumAttendees && amountOfAttendeesForEvent >= event.maximumAttendees) {\n        throw new BadRequest('The maximum number of attendees for this event has been reached.')\n      }\n\n      const attendee = await prisma.attendee.create({\n        data: {\n          name,\n          email,\n          eventId,\n        }\n      })\n\n      return reply.status(201).send({ attendeeId: attendee.id })\n    })\n}"
  },
  {
    "path": "src/server.ts",
    "content": "import fastify from \"fastify\";\n\nimport fastifySwagger from \"@fastify/swagger\";\nimport fastifySwaggerUI from \"@fastify/swagger-ui\";\nimport fastifyCors from \"@fastify/cors\";\n\nimport { serializerCompiler, validatorCompiler, jsonSchemaTransform, ZodTypeProvider } from 'fastify-type-provider-zod'\nimport { createEvent } from \"./routes/create-event\";\nimport { registerForEvent } from \"./routes/register-for-event\";\nimport { getEvent } from \"./routes/get-event\";\nimport { getAttendeeBadge } from \"./routes/get-attendee-badge\";\nimport { checkIn } from \"./routes/check-in\";\nimport { getEventAttendees } from \"./routes/get-event-attendees\";\nimport { errorHandler } from \"./error-handler\";\n\nexport const app = fastify().withTypeProvider<ZodTypeProvider>()\n\napp.register(fastifyCors, {\n  origin: '*',\n})\n\napp.register(fastifySwagger, {\n  swagger: {\n    consumes: ['application/json'],\n    produces: ['application/json'],\n    info: {\n      title: 'pass.in',\n      description: 'Especificações da API para o back-end da aplicação pass.in construída durante o NLW Unite da Rocketseat.',\n      version: '1.0.0'\n    },\n  },\n  transform: jsonSchemaTransform,\n})\n\napp.register(fastifySwaggerUI, {\n  routePrefix: '/docs',\n})\n\napp.setValidatorCompiler(validatorCompiler);\napp.setSerializerCompiler(serializerCompiler);\n\napp.register(createEvent)\napp.register(registerForEvent)\napp.register(getEvent)\napp.register(getAttendeeBadge)\napp.register(checkIn)\napp.register(getEventAttendees)\n\napp.setErrorHandler(errorHandler)\n\napp.listen({ port: 3333, host: '0.0.0.0' }).then(() => {\n  console.log('HTTP server running!')\n})\n"
  },
  {
    "path": "src/utils/generate-slug.ts",
    "content": "export function generateSlug(text: string): string {\n  return text\n    .normalize(\"NFD\")\n    .replace(/[\\u0300-\\u036f]/g, \"\")\n    .toLowerCase()\n    .replace(/[^\\w\\s-]/g, \"\")\n    .replace(/\\s+/g, \"-\");\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Node 20\",\n  \"_version\": \"20.1.0\",\n\n  \"compilerOptions\": {\n    \"lib\": [\"es2023\"],\n    \"module\": \"node16\",\n    \"target\": \"es2022\",\n\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true,\n    \"moduleResolution\": \"node16\"\n  },\n\n  \"include\": [\"src\"]\n}"
  }
]