main 61445f96837f cached
24 files
21.9 KB
6.4k tokens
20 symbols
1 requests
Download .txt
Repository: rocketseat-education/nlw-unite-nodejs
Branch: main
Commit: 61445f96837f
Files: 24
Total size: 21.9 KB

Directory structure:
gitextract__xl0pikx/

├── .gitignore
├── README.md
├── api.http
├── package.json
├── prisma/
│   ├── migrations/
│   │   ├── 20240325193227_create_table_events/
│   │   │   └── migration.sql
│   │   ├── 20240326182647_create_table_attendees/
│   │   │   └── migration.sql
│   │   ├── 20240326184908_add_uniqueness_on_event_id_and_email/
│   │   │   └── migration.sql
│   │   ├── 20240327140503_create_check_ins_table/
│   │   │   └── migration.sql
│   │   ├── 20240327143815_add_cascades/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   ├── schema.prisma
│   └── seed.ts
├── src/
│   ├── error-handler.ts
│   ├── lib/
│   │   └── prisma.ts
│   ├── routes/
│   │   ├── _errors/
│   │   │   └── bad-request.ts
│   │   ├── check-in.ts
│   │   ├── create-event.ts
│   │   ├── get-attendee-badge.ts
│   │   ├── get-event-attendees.ts
│   │   ├── get-event.ts
│   │   └── register-for-event.ts
│   ├── server.ts
│   └── utils/
│       └── generate-slug.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules
# Keep environment variables out of version control
.env
dist

================================================
FILE: README.md
================================================
# pass.in

O pass.in é uma aplicação de **gestão de participantes em eventos presenciais**. 

A ferramenta permite que o organizador cadastre um evento e abra uma página pública de inscrição.

Os participantes inscritos podem emitir uma credencial para check-in no dia do evento.

O sistema fará um scan da credencial do participante para permitir a entrada no evento.

## Requisitos

### Requisitos funcionais

- [x] O organizador deve poder cadastrar um novo evento;
- [x] O organizador deve poder visualizar dados de um evento;
- [x] O organizador deve poser visualizar a lista de participantes; 
- [x] O participante deve poder se inscrever em um evento;
- [x] O participante deve poder visualizar seu crachá de inscrição;
- [x] O participante deve poder realizar check-in no evento;

### Regras de negócio

- [x] O participante só pode se inscrever em um evento uma única vez;
- [x] O participante só pode se inscrever em eventos com vagas disponíveis;
- [x] O participante só pode realizar check-in em um evento uma única vez;

### Requisitos não-funcionais

- [x] O check-in no evento será realizado através de um QRCode;

## Documentação da API (Swagger)

Para documentação da API, acesse o link: https://nlw-unite-nodejs.onrender.com/docs

## Banco de dados

Nessa aplicação vamos utilizar banco de dados relacional (SQL). Para ambiente de desenvolvimento seguiremos com o SQLite pela facilidade do ambiente.

### Diagrama ERD

<img src=".github/erd.svg" width="600" alt="Diagrama ERD do banco de dados" />

### Estrutura do banco (SQL)

```sql
-- CreateTable
CREATE TABLE "events" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "title" TEXT NOT NULL,
    "details" TEXT,
    "slug" TEXT NOT NULL,
    "maximum_attendees" INTEGER
);

-- CreateTable
CREATE TABLE "attendees" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    "event_id" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT "attendees_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "events" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "check_ins" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "attendeeId" INTEGER NOT NULL,
    CONSTRAINT "check_ins_attendeeId_fkey" FOREIGN KEY ("attendeeId") REFERENCES "attendees" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateIndex
CREATE UNIQUE INDEX "events_slug_key" ON "events"("slug");

-- CreateIndex
CREATE UNIQUE INDEX "attendees_event_id_email_key" ON "attendees"("event_id", "email");

-- CreateIndex
CREATE UNIQUE INDEX "check_ins_attendeeId_key" ON "check_ins"("attendeeId");
```

================================================
FILE: api.http
================================================
POST http://localhost:3333/events
Content-Type: application/json

{
  "title": 123,
  "details": null,
  "maximumAttendees": 1
}

###

POST http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43/attendees
Content-Type: application/json

{
  "name": "Diego Fernandes",
  "email": "diego2@rocketseat.com.br"
}

###

GET http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43

###

GET http://localhost:3333/attendees/3/badge
###

GET http://localhost:3333/attendees/4/check-in

###

GET http://localhost:3333/events/81a3f6db-45bf-4a6f-b10c-bcaf8837ac43/attendees?query=diego

================================================
FILE: package.json
================================================
{
  "name": "server-node",
  "version": "1.0.0",
  "description": "O pass.in é uma aplicação de **gestão de participantes em eventos presenciais**.",
  "main": "index.js",
  "scripts": {
    "start": "node dist/server.mjs",
    "build": "tsup src --format esm",
    "dev": "tsx watch --env-file .env src/server.ts",
    "db:migrate": "prisma migrate dev",
    "db:studio": "prisma studio"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "prisma": {
    "seed": "tsx prisma/seed.ts"
  },
  "devDependencies": {
    "@faker-js/faker": "8.4.1",
    "@types/node": "20.11.30",
    "prisma": "5.11.0",
    "tsup": "8.0.2",
    "tsx": "4.7.1",
    "typescript": "5.4.3"
  },
  "dependencies": {
    "@fastify/cors": "9.0.1",
    "@fastify/swagger": "8.14.0",
    "@fastify/swagger-ui": "3.0.0",
    "@prisma/client": "5.11.0",
    "dayjs": "1.11.10",
    "fastify": "4.26.2",
    "fastify-type-provider-zod": "1.1.9",
    "zod": "3.22.4"
  }
}


================================================
FILE: prisma/migrations/20240325193227_create_table_events/migration.sql
================================================
-- CreateTable
CREATE TABLE "events" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "title" TEXT NOT NULL,
    "details" TEXT,
    "slug" TEXT NOT NULL,
    "maximum_attendees" INTEGER
);

-- CreateIndex
CREATE UNIQUE INDEX "events_slug_key" ON "events"("slug");


================================================
FILE: prisma/migrations/20240326182647_create_table_attendees/migration.sql
================================================
-- CreateTable
CREATE TABLE "attendees" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "event_id" TEXT NOT NULL,
    CONSTRAINT "attendees_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "events" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);


================================================
FILE: prisma/migrations/20240326184908_add_uniqueness_on_event_id_and_email/migration.sql
================================================
/*
  Warnings:

  - 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.

*/
-- CreateIndex
CREATE UNIQUE INDEX "attendees_event_id_email_key" ON "attendees"("event_id", "email");


================================================
FILE: prisma/migrations/20240327140503_create_check_ins_table/migration.sql
================================================
-- CreateTable
CREATE TABLE "check_ins" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "attendee_id" INTEGER NOT NULL,
    CONSTRAINT "check_ins_attendee_id_fkey" FOREIGN KEY ("attendee_id") REFERENCES "attendees" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateIndex
CREATE UNIQUE INDEX "check_ins_attendee_id_key" ON "check_ins"("attendee_id");


================================================
FILE: prisma/migrations/20240327143815_add_cascades/migration.sql
================================================
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_check_ins" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "attendee_id" INTEGER NOT NULL,
    CONSTRAINT "check_ins_attendee_id_fkey" FOREIGN KEY ("attendee_id") REFERENCES "attendees" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_check_ins" ("attendee_id", "created_at", "id") SELECT "attendee_id", "created_at", "id" FROM "check_ins";
DROP TABLE "check_ins";
ALTER TABLE "new_check_ins" RENAME TO "check_ins";
CREATE UNIQUE INDEX "check_ins_attendee_id_key" ON "check_ins"("attendee_id");
CREATE TABLE "new_attendees" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "event_id" TEXT NOT NULL,
    CONSTRAINT "attendees_event_id_fkey" FOREIGN KEY ("event_id") REFERENCES "events" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_attendees" ("created_at", "email", "event_id", "id", "name") SELECT "created_at", "email", "event_id", "id", "name" FROM "attendees";
DROP TABLE "attendees";
ALTER TABLE "new_attendees" RENAME TO "attendees";
CREATE UNIQUE INDEX "attendees_event_id_email_key" ON "attendees"("event_id", "email");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;


================================================
FILE: prisma/migrations/migration_lock.toml
================================================
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "sqlite"

================================================
FILE: prisma/schema.prisma
================================================
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Event {
  id               String  @id @default(uuid())
  title            String
  details          String?
  slug             String  @unique
  maximumAttendees Int?    @map("maximum_attendees")

  attendees Attendee[]

  @@map("events")
}

model Attendee {
  id        Int      @id @default(autoincrement())
  name      String
  email     String
  createdAt DateTime @default(now()) @map("created_at")
  eventId   String   @map("event_id")

  event   Event    @relation(fields: [eventId], references: [id], onDelete: Cascade)
  checkIn CheckIn?

  @@unique([eventId, email])
  @@map("attendees")
}

model CheckIn {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")

  attendee   Attendee @relation(fields: [attendeeId], references: [id], onDelete: Cascade)
  attendeeId Int      @unique @map("attendee_id")

  @@map("check_ins")
}


================================================
FILE: prisma/seed.ts
================================================
import { prisma } from '../src/lib/prisma'
import { faker } from '@faker-js/faker'
import { Prisma } from '@prisma/client'
import dayjs from 'dayjs'

async function seed() {
  const eventId = '9e9bd979-9d10-4915-b339-3786b1634f33'

  await prisma.event.deleteMany()

  await prisma.event.create({
    data: {
      id: eventId,
      title: 'Unite Summit',
      slug: 'unite-summit',
      details: 'Um evento p/ devs apaixonados(as) por código!',
      maximumAttendees: 120,
    }
  })

  const attendeesToInsert: Prisma.AttendeeUncheckedCreateInput[] = []

  for (let i = 0; i <= 120; i++) {
    attendeesToInsert.push({
      id: 10000 + i,
      name: faker.person.fullName(),
      email: faker.internet.email(),
      eventId,
      createdAt: faker.date.recent({ days: 30, refDate: dayjs().subtract(8, "days").toDate() }),
      checkIn: faker.helpers.arrayElement<Prisma.CheckInUncheckedCreateNestedOneWithoutAttendeeInput | undefined>([
        undefined,
        {
          create: {
            createdAt: faker.date.recent({ days: 7 }),
          }
        }
      ])
    })
  }

  await Promise.all(attendeesToInsert.map(data => {
    return prisma.attendee.create({
      data,
    })
  }))
}

seed().then(() => {
  console.log('Database seeded!')
  prisma.$disconnect()
})

================================================
FILE: src/error-handler.ts
================================================
import { FastifyInstance } from 'fastify'
import { BadRequest } from './routes/_errors/bad-request'
import { ZodError } from 'zod'

type FastifyErrorHandler = FastifyInstance['errorHandler']

export const errorHandler: FastifyErrorHandler = (error, request, reply) => {
  if (error instanceof ZodError) {
    return reply.status(400).send({
      message: 'Error during validation',
      errors: error.flatten().fieldErrors,
    })
  }

  if (error instanceof BadRequest) {
    return reply.status(400).send({ message: error.message })
  }

  return reply.status(500).send({ message: 'Internal server error!' })
}

================================================
FILE: src/lib/prisma.ts
================================================
import { PrismaClient } from "@prisma/client";

export const prisma = new PrismaClient({
  log: ['query'],
})

================================================
FILE: src/routes/_errors/bad-request.ts
================================================
export class BadRequest extends Error {}

================================================
FILE: src/routes/check-in.ts
================================================
import { FastifyInstance } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import z from "zod";
import { prisma } from "../lib/prisma";
import { BadRequest } from "./_errors/bad-request";

export async function checkIn(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .get('/attendees/:attendeeId/check-in', {
      schema: {
        summary: 'Check-in an attendee',
        tags: ['check-ins'],
        params: z.object({
          attendeeId: z.coerce.number().int()
        }),
        response: {
          201: z.null(),
        }
      }
    }, async (request, reply) => {
      const { attendeeId } = request.params

      const attendeeCheckIn = await prisma.checkIn.findUnique({
        where: {
          attendeeId,
        }
      })

      if (attendeeCheckIn !== null) {
        throw new BadRequest('Attendee already checked in!')
      }

      await prisma.checkIn.create({
        data: {
          attendeeId,
        }
      })

      return reply.status(201).send()
    })
}

================================================
FILE: src/routes/create-event.ts
================================================
import { ZodTypeProvider } from "fastify-type-provider-zod"
import { z } from "zod"
import { generateSlug } from "../utils/generate-slug"
import { prisma } from "../lib/prisma"
import { FastifyInstance } from "fastify"
import { BadRequest } from "./_errors/bad-request"

export async function createEvent(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .post('/events', {
      schema: {
        summary: 'Create an event',
        tags: ['events'],
        body: z.object({
          title: z.string().min(4),
          details: z.string().nullable(),
          maximumAttendees: z.number().int().positive().nullable(),
        }),
        response: {
          201: z.object({
            eventId: z.string().uuid(),
          })
        },
      },
    }, async (request, reply) => {
      const {
        title,
        details,
        maximumAttendees,
      } = request.body

      const slug = generateSlug(title)

      const eventWithSameSlug = await prisma.event.findUnique({
        where: {
          slug,
        }
      })

      if (eventWithSameSlug !== null) {
        throw new BadRequest('Another event with same title already exists.')
      }

      const event = await prisma.event.create({
        data: {
          title,
          details,
          maximumAttendees,
          slug,
        },
      })

      return reply.status(201).send({ eventId: event.id })
    })
}



================================================
FILE: src/routes/get-attendee-badge.ts
================================================
import { FastifyInstance } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import { z } from "zod";
import { prisma } from "../lib/prisma";
import { BadRequest } from "./_errors/bad-request";

export async function getAttendeeBadge(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .get('/attendees/:attendeeId/badge', {
      schema: {
        summary: 'Get an attendee badge',
        tags: ['attendees'],
        params: z.object({
          attendeeId: z.coerce.number().int(),
        }),
        response: {
          200: z.object({
            badge: z.object({
              name: z.string(),
              email: z.string().email(),
              eventTitle: z.string(),
              checkInURL: z.string().url(),
            })
          })
        },
      }
    }, async (request, reply) => {
      const { attendeeId } = request.params

      const attendee = await prisma.attendee.findUnique({
        select: {
          name: true,
          email: true,
          event: {
            select: {
              title: true,
            },
          },
        },
        where: {
          id: attendeeId,
        }
      })

      if (attendee === null) {
        throw new BadRequest('Attendee not found.')
      }

      const baseURL = `${request.protocol}://${request.hostname}`

      const checkInURL = new URL(`/attendees/${attendeeId}/check-in`, baseURL)

      return reply.send({
        badge: {
          name: attendee.name,
          email: attendee.email,
          eventTitle: attendee.event.title,
          checkInURL: checkInURL.toString(),
        }
      })
    })
}

================================================
FILE: src/routes/get-event-attendees.ts
================================================
import { FastifyInstance } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import { z } from "zod";
import { prisma } from "../lib/prisma";

export async function getEventAttendees(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .get('/events/:eventId/attendees', {
      schema: {
        summary: 'Get event attendees',
        tags: ['events'],
        params: z.object({
          eventId: z.string().uuid(),
        }),
        querystring: z.object({
          query: z.string().nullish(),
          pageIndex: z.string().nullish().default('0').transform(Number),
        }),
        response: {
          200: z.object({
            attendees: z.array(
              z.object({
                id: z.number(),
                name: z.string(),
                email: z.string().email(),
                createdAt: z.date(),
                checkedInAt: z.date().nullable(),
              })
            ),
            total: z.number(),
          }),
        },
      }
    }, async (request, reply) => {
      const { eventId } = request.params
      const { pageIndex, query } = request.query

      const [attendees, total] = await Promise.all([
        prisma.attendee.findMany({
          select: {
            id: true,
            name: true,
            email: true,
            createdAt: true,
            checkIn: {
              select: {
                createdAt: true,
              }
            }
          },
          where: query ? {
            eventId,
            name: {
              contains: query,
            }
          } : {
            eventId,
          },
          take: 10,
          skip: pageIndex * 10,
          orderBy: {
            createdAt: 'desc'
          }
        }),
        prisma.attendee.count({
          where: query ? {
            eventId,
            name: {
              contains: query,
            }
          } : {
            eventId,
          },
        })
      ])

      return reply.send({ 
        attendees: attendees.map(attendee => {
          return {
            id: attendee.id,
            name: attendee.name,
            email: attendee.email,
            createdAt: attendee.createdAt,
            checkedInAt: attendee.checkIn?.createdAt ?? null,
          }
        }),
        total,
      })
    })
}

================================================
FILE: src/routes/get-event.ts
================================================
import { FastifyInstance } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import { z } from "zod";
import { prisma } from "../lib/prisma";
import { BadRequest } from "./_errors/bad-request";

export async function getEvent(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .get('/events/:eventId', {
      schema: {
        summary: 'Get an event',
        tags: ['events'],
        params: z.object({
          eventId: z.string().uuid(),
        }),
        response: {
          200: z.object({
            event: z.object({
              id: z.string().uuid(),
              title: z.string(),
              slug: z.string(),
              details: z.string().nullable(),
              maximumAttendees: z.number().int().nullable(),
              attendeesAmount: z.number().int(),
            })
          }),
        },
      }
    }, async (request, reply) => {
      const { eventId } = request.params

      const event = await prisma.event.findUnique({
        select: {
          id: true,
          title: true,
          slug: true,
          details: true,
          maximumAttendees: true,
          _count: {
            select: {
              attendees: true,
            }
          },
        },
        where: {
          id: eventId,
        }
      })

      if (event === null) {
        throw new BadRequest('Event not found.')
      }

      return reply.send({ 
        event: {
          id: event.id,
          title: event.title,
          slug: event.slug,
          details: event.details,
          maximumAttendees: event.maximumAttendees,
          attendeesAmount: event._count.attendees,
        },
      })
    })
}

================================================
FILE: src/routes/register-for-event.ts
================================================
import { FastifyInstance } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import { z } from "zod";
import { prisma } from "../lib/prisma";
import { BadRequest } from "./_errors/bad-request";

export async function registerForEvent(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .post('/events/:eventId/attendees', {
      schema: {
        summary: 'Register an attendee',
        tags: ['attendees'],
        body: z.object({
          name: z.string().min(4),
          email: z.string().email(),
        }),
        params: z.object({
          eventId: z.string().uuid(),
        }),
        response: {
          201: z.object({
            attendeeId: z.number(),
          })
        }
      }
    }, async (request, reply) => {
      const { eventId } = request.params
      const { name, email } = request.body

      const attendeeFromEmail = await prisma.attendee.findUnique({
        where: {
          eventId_email: {
            email,
            eventId
          }
        }
      })

      if (attendeeFromEmail !== null) {
        throw new BadRequest('This e-mail is already registered for this event.')
      }

      const [event, amountOfAttendeesForEvent] = await Promise.all([
        prisma.event.findUnique({
          where: {
            id: eventId,
          }
        }),

        prisma.attendee.count({
          where: {
            eventId,
          }
        })
      ])

      if (event?.maximumAttendees && amountOfAttendeesForEvent >= event.maximumAttendees) {
        throw new BadRequest('The maximum number of attendees for this event has been reached.')
      }

      const attendee = await prisma.attendee.create({
        data: {
          name,
          email,
          eventId,
        }
      })

      return reply.status(201).send({ attendeeId: attendee.id })
    })
}

================================================
FILE: src/server.ts
================================================
import fastify from "fastify";

import fastifySwagger from "@fastify/swagger";
import fastifySwaggerUI from "@fastify/swagger-ui";
import fastifyCors from "@fastify/cors";

import { serializerCompiler, validatorCompiler, jsonSchemaTransform, ZodTypeProvider } from 'fastify-type-provider-zod'
import { createEvent } from "./routes/create-event";
import { registerForEvent } from "./routes/register-for-event";
import { getEvent } from "./routes/get-event";
import { getAttendeeBadge } from "./routes/get-attendee-badge";
import { checkIn } from "./routes/check-in";
import { getEventAttendees } from "./routes/get-event-attendees";
import { errorHandler } from "./error-handler";

export const app = fastify().withTypeProvider<ZodTypeProvider>()

app.register(fastifyCors, {
  origin: '*',
})

app.register(fastifySwagger, {
  swagger: {
    consumes: ['application/json'],
    produces: ['application/json'],
    info: {
      title: 'pass.in',
      description: 'Especificações da API para o back-end da aplicação pass.in construída durante o NLW Unite da Rocketseat.',
      version: '1.0.0'
    },
  },
  transform: jsonSchemaTransform,
})

app.register(fastifySwaggerUI, {
  routePrefix: '/docs',
})

app.setValidatorCompiler(validatorCompiler);
app.setSerializerCompiler(serializerCompiler);

app.register(createEvent)
app.register(registerForEvent)
app.register(getEvent)
app.register(getAttendeeBadge)
app.register(checkIn)
app.register(getEventAttendees)

app.setErrorHandler(errorHandler)

app.listen({ port: 3333, host: '0.0.0.0' }).then(() => {
  console.log('HTTP server running!')
})


================================================
FILE: src/utils/generate-slug.ts
================================================
export function generateSlug(text: string): string {
  return text
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^\w\s-]/g, "")
    .replace(/\s+/g, "-");
}

================================================
FILE: tsconfig.json
================================================
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Node 20",
  "_version": "20.1.0",

  "compilerOptions": {
    "lib": ["es2023"],
    "module": "node16",
    "target": "es2022",

    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "moduleResolution": "node16"
  },

  "include": ["src"]
}
Download .txt
gitextract__xl0pikx/

├── .gitignore
├── README.md
├── api.http
├── package.json
├── prisma/
│   ├── migrations/
│   │   ├── 20240325193227_create_table_events/
│   │   │   └── migration.sql
│   │   ├── 20240326182647_create_table_attendees/
│   │   │   └── migration.sql
│   │   ├── 20240326184908_add_uniqueness_on_event_id_and_email/
│   │   │   └── migration.sql
│   │   ├── 20240327140503_create_check_ins_table/
│   │   │   └── migration.sql
│   │   ├── 20240327143815_add_cascades/
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   ├── schema.prisma
│   └── seed.ts
├── src/
│   ├── error-handler.ts
│   ├── lib/
│   │   └── prisma.ts
│   ├── routes/
│   │   ├── _errors/
│   │   │   └── bad-request.ts
│   │   ├── check-in.ts
│   │   ├── create-event.ts
│   │   ├── get-attendee-badge.ts
│   │   ├── get-event-attendees.ts
│   │   ├── get-event.ts
│   │   └── register-for-event.ts
│   ├── server.ts
│   └── utils/
│       └── generate-slug.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (20 symbols across 15 files)

FILE: prisma/migrations/20240325193227_create_table_events/migration.sql
  type "events" (line 2) | CREATE TABLE "events" (
  type "events" (line 11) | CREATE UNIQUE INDEX "events_slug_key" ON "events"("slug")

FILE: prisma/migrations/20240326182647_create_table_attendees/migration.sql
  type "attendees" (line 2) | CREATE TABLE "attendees" (

FILE: prisma/migrations/20240326184908_add_uniqueness_on_event_id_and_email/migration.sql
  type "attendees" (line 8) | CREATE UNIQUE INDEX "attendees_event_id_email_key" ON "attendees"("event...

FILE: prisma/migrations/20240327140503_create_check_ins_table/migration.sql
  type "check_ins" (line 2) | CREATE TABLE "check_ins" (
  type "check_ins" (line 10) | CREATE UNIQUE INDEX "check_ins_attendee_id_key" ON "check_ins"("attendee...

FILE: prisma/migrations/20240327143815_add_cascades/migration.sql
  type "new_check_ins" (line 3) | CREATE TABLE "new_check_ins" (
  type "check_ins" (line 12) | CREATE UNIQUE INDEX "check_ins_attendee_id_key" ON "check_ins"("attendee...
  type "new_attendees" (line 13) | CREATE TABLE "new_attendees" (
  type "attendees" (line 24) | CREATE UNIQUE INDEX "attendees_event_id_email_key" ON "attendees"("event...

FILE: prisma/seed.ts
  function seed (line 6) | async function seed() {

FILE: src/error-handler.ts
  type FastifyErrorHandler (line 5) | type FastifyErrorHandler = FastifyInstance['errorHandler']

FILE: src/routes/_errors/bad-request.ts
  class BadRequest (line 1) | class BadRequest extends Error {}

FILE: src/routes/check-in.ts
  function checkIn (line 7) | async function checkIn(app: FastifyInstance) {

FILE: src/routes/create-event.ts
  function createEvent (line 8) | async function createEvent(app: FastifyInstance) {

FILE: src/routes/get-attendee-badge.ts
  function getAttendeeBadge (line 7) | async function getAttendeeBadge(app: FastifyInstance) {

FILE: src/routes/get-event-attendees.ts
  function getEventAttendees (line 6) | async function getEventAttendees(app: FastifyInstance) {

FILE: src/routes/get-event.ts
  function getEvent (line 7) | async function getEvent(app: FastifyInstance) {

FILE: src/routes/register-for-event.ts
  function registerForEvent (line 7) | async function registerForEvent(app: FastifyInstance) {

FILE: src/utils/generate-slug.ts
  function generateSlug (line 1) | function generateSlug(text: string): string {
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (25K chars).
[
  {
    "path": ".gitignore",
    "chars": 74,
    "preview": "node_modules\n# Keep environment variables out of version control\n.env\ndist"
  },
  {
    "path": "README.md",
    "chars": 2719,
    "preview": "# pass.in\n\nO pass.in é uma aplicação de **gestão de participantes em eventos presenciais**. \n\nA ferramenta permite que o"
  },
  {
    "path": "api.http",
    "chars": 594,
    "preview": "POST http://localhost:3333/events\nContent-Type: application/json\n\n{\n  \"title\": 123,\n  \"details\": null,\n  \"maximumAttende"
  },
  {
    "path": "package.json",
    "chars": 951,
    "preview": "{\n  \"name\": \"server-node\",\n  \"version\": \"1.0.0\",\n  \"description\": \"O pass.in é uma aplicação de **gestão de participante"
  },
  {
    "path": "prisma/migrations/20240325193227_create_table_events/migration.sql",
    "chars": 258,
    "preview": "-- CreateTable\nCREATE TABLE \"events\" (\n    \"id\" TEXT NOT NULL PRIMARY KEY,\n    \"title\" TEXT NOT NULL,\n    \"details\" TEXT"
  },
  {
    "path": "prisma/migrations/20240326182647_create_table_attendees/migration.sql",
    "chars": 373,
    "preview": "-- CreateTable\nCREATE TABLE \"attendees\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"name\" TEXT NOT NULL,"
  },
  {
    "path": "prisma/migrations/20240326184908_add_uniqueness_on_event_id_and_email/migration.sql",
    "chars": 283,
    "preview": "/*\n  Warnings:\n\n  - A unique constraint covering the columns `[event_id,email]` on the table `attendees` will be added. "
  },
  {
    "path": "prisma/migrations/20240327140503_create_check_ins_table/migration.sql",
    "chars": 430,
    "preview": "-- CreateTable\nCREATE TABLE \"check_ins\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"created_at\" DATETIME"
  },
  {
    "path": "prisma/migrations/20240327143815_add_cascades/migration.sql",
    "chars": 1368,
    "preview": "-- RedefineTables\nPRAGMA foreign_keys=OFF;\nCREATE TABLE \"new_check_ins\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCR"
  },
  {
    "path": "prisma/migrations/migration_lock.toml",
    "chars": 122,
    "preview": "# Please do not edit this file manually\n# It should be added in your version-control system (i.e. Git)\nprovider = \"sqlit"
  },
  {
    "path": "prisma/schema.prisma",
    "chars": 1025,
    "preview": "generator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_U"
  },
  {
    "path": "prisma/seed.ts",
    "chars": 1290,
    "preview": "import { prisma } from '../src/lib/prisma'\nimport { faker } from '@faker-js/faker'\nimport { Prisma } from '@prisma/clien"
  },
  {
    "path": "src/error-handler.ts",
    "chars": 614,
    "preview": "import { FastifyInstance } from 'fastify'\nimport { BadRequest } from './routes/_errors/bad-request'\nimport { ZodError } "
  },
  {
    "path": "src/lib/prisma.ts",
    "chars": 109,
    "preview": "import { PrismaClient } from \"@prisma/client\";\n\nexport const prisma = new PrismaClient({\n  log: ['query'],\n})"
  },
  {
    "path": "src/routes/_errors/bad-request.ts",
    "chars": 40,
    "preview": "export class BadRequest extends Error {}"
  },
  {
    "path": "src/routes/check-in.ts",
    "chars": 1050,
    "preview": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport z from \"z"
  },
  {
    "path": "src/routes/create-event.ts",
    "chars": 1428,
    "preview": "import { ZodTypeProvider } from \"fastify-type-provider-zod\"\nimport { z } from \"zod\"\nimport { generateSlug } from \"../uti"
  },
  {
    "path": "src/routes/get-attendee-badge.ts",
    "chars": 1659,
    "preview": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } fro"
  },
  {
    "path": "src/routes/get-event-attendees.ts",
    "chars": 2347,
    "preview": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } fro"
  },
  {
    "path": "src/routes/get-event.ts",
    "chars": 1708,
    "preview": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } fro"
  },
  {
    "path": "src/routes/register-for-event.ts",
    "chars": 1882,
    "preview": "import { FastifyInstance } from \"fastify\";\nimport { ZodTypeProvider } from \"fastify-type-provider-zod\";\nimport { z } fro"
  },
  {
    "path": "src/server.ts",
    "chars": 1599,
    "preview": "import fastify from \"fastify\";\n\nimport fastifySwagger from \"@fastify/swagger\";\nimport fastifySwaggerUI from \"@fastify/sw"
  },
  {
    "path": "src/utils/generate-slug.ts",
    "chars": 203,
    "preview": "export function generateSlug(text: string): string {\n  return text\n    .normalize(\"NFD\")\n    .replace(/[\\u0300-\\u036f]/g"
  },
  {
    "path": "tsconfig.json",
    "chars": 336,
    "preview": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Node 20\",\n  \"_version\": \"20.1.0\",\n\n  \"compilerOpti"
  }
]

About this extraction

This page contains the full source code of the rocketseat-education/nlw-unite-nodejs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (21.9 KB), approximately 6.4k tokens, and a symbol index with 20 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!