feat: add Prisma ORM to backend with spaces migration and PrismaService

This commit is contained in:
Jessica Orozco 2026-06-30 21:03:45 -04:00
parent 71a4c63926
commit 08eddc1d9a
10 changed files with 114 additions and 33 deletions

View File

@ -6,7 +6,9 @@
"build": "nest build",
"start": "nest start",
"start:dev": "nest start --watch",
"start:prod": "node dist/main"
"start:prod": "node dist/main",
"db:migrate": "prisma migrate deploy",
"db:generate": "prisma generate"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
@ -14,6 +16,7 @@
"@nestjs/jwt": "^10.0.0",
"@nestjs/passport": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@prisma/client": "^5.0.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.1.13",
@ -24,6 +27,7 @@
"@nestjs/schematics": "^10.0.0",
"@types/node": "^20.0.0",
"@types/passport-jwt": "^4.0.0",
"prisma": "^5.0.0",
"typescript": "^5.0.0"
}
}

View File

@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "reservas" (
"id" TEXT NOT NULL,
"resourceId" TEXT NOT NULL,
"resourceName" TEXT NOT NULL,
"date" TEXT NOT NULL,
"startTime" TEXT NOT NULL,
"endTime" TEXT NOT NULL,
"author" TEXT NOT NULL,
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "reservas_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "reservas_resourceName_date_startTime_key" ON "reservas"("resourceName", "date", "startTime");

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View File

@ -0,0 +1,23 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Reserva {
id String @id @default(cuid())
resourceId String
resourceName String
date String // "YYYY-MM-DD"
startTime String // "HH:mm"
endTime String // "HH:mm"
author String
notes String?
createdAt DateTime @default(now())
@@unique([resourceName, date, startTime])
@@map("reservas")
}

View File

@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma/prisma.module';
import { AuthModule } from './auth/auth.module';
import { CmsModule } from './cms/cms.module';
import { DirectoryModule } from './directory/directory.module';
@ -8,6 +9,7 @@ import { TicketingModule } from './ticketing/ticketing.module';
@Module({
imports: [
PrismaModule,
AuthModule,
CmsModule,
DirectoryModule,
@ -17,3 +19,4 @@ import { TicketingModule } from './ticketing/ticketing.module';
],
})
export class AppModule {}

View File

@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}

View File

@ -0,0 +1,16 @@
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}

View File

@ -19,7 +19,7 @@ export class SpacesController {
* Devuelve todas las reservas (opcionalmente filtradas por fecha)
*/
@Get()
findAll(@Query('date') date?: string) {
async findAll(@Query('date') date?: string) {
return this.spacesService.findAll(date);
}
@ -28,7 +28,7 @@ export class SpacesController {
* Crea una nueva reserva
*/
@Post()
create(@Body() dto: CreateReservaDto) {
async create(@Body() dto: CreateReservaDto) {
return this.spacesService.create(dto);
}
@ -37,8 +37,8 @@ export class SpacesController {
* Elimina una reserva por ID
*/
@Delete(':id')
delete(@Param('id') id: string) {
this.spacesService.delete(id);
async delete(@Param('id') id: string) {
await this.spacesService.delete(id);
return { message: 'Reserva eliminada correctamente.' };
}
}

View File

@ -1,4 +1,9 @@
import { Module } from '@nestjs/common';
import { SpacesController } from './spaces.controller';
import { SpacesService } from './spaces.service';
@Module({})
@Module({
controllers: [SpacesController],
providers: [SpacesService],
})
export class SpacesModule {}

View File

@ -1,26 +1,28 @@
import { Injectable, ConflictException } from '@nestjs/common';
import { CreateReservaDto, Reserva } from './reserva.dto';
import { Injectable, ConflictException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateReservaDto } from './reserva.dto';
@Injectable()
export class SpacesService {
private reservas: Reserva[] = [];
private idCounter = 1;
constructor(private readonly prisma: PrismaService) {}
findAll(date?: string): Reserva[] {
if (date) {
return this.reservas.filter((r) => r.date === date);
}
return this.reservas;
async findAll(date?: string) {
return this.prisma.reserva.findMany({
where: date ? { date } : undefined,
orderBy: { createdAt: 'desc' },
});
}
create(dto: CreateReservaDto): Reserva {
// Verificar conflicto: mismo recurso, mismo día, mismo horario de inicio
const conflict = this.reservas.find(
(r) =>
r.resourceName === dto.resourceName &&
r.date === dto.date &&
r.startTime === dto.startTime,
);
async create(dto: CreateReservaDto) {
const conflict = await this.prisma.reserva.findUnique({
where: {
resourceName_date_startTime: {
resourceName: dto.resourceName,
date: dto.date,
startTime: dto.startTime,
},
},
});
if (conflict) {
throw new ConflictException(
@ -28,17 +30,16 @@ export class SpacesService {
);
}
const newReserva: Reserva = {
...dto,
id: String(this.idCounter++),
createdAt: new Date().toISOString(),
};
this.reservas.unshift(newReserva);
return newReserva;
return this.prisma.reserva.create({ data: dto });
}
delete(id: string): void {
this.reservas = this.reservas.filter((r) => r.id !== id);
async delete(id: string): Promise<void> {
const reserva = await this.prisma.reserva.findUnique({ where: { id } });
if (!reserva) {
throw new NotFoundException(`Reserva con id "${id}" no encontrada.`);
}
await this.prisma.reserva.delete({ where: { id } });
}
}