From cdaf5780e5f764f715fb91818a423a16e0455fab Mon Sep 17 00:00:00 2001 From: Robert Perez Date: Sat, 11 Jul 2026 18:37:34 +0000 Subject: [PATCH] fix: Add missing relations to Prisma schema (Pet/Booking, Invoice/Booking) --- api/Dockerfile | 13 +++++ api/prisma/schema.prisma | 117 +++++++++++++++++++++------------------ api/src/main.ts | 4 +- docker-compose.yml | 19 +------ web/Dockerfile | 12 ++++ 5 files changed, 92 insertions(+), 73 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 9f35df6..0dc022f 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -9,6 +9,19 @@ COPY . . RUN npx prisma generate RUN npm run build +FROM node:22-alpine AS dev + +WORKDIR /app + +COPY package.json yarn.lock* ./ +RUN yarn install --frozen-lockfile 2>/dev/null || npm install + +COPY . . +RUN npx prisma generate + +EXPOSE 3000 +CMD ["npm", "run", "start:dev"] + FROM node:22-alpine WORKDIR /app diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 508599d..be12aa5 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -1,3 +1,12 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + model Client { id String @id @default(cuid()) createdAt DateTime @default(now()) @@ -15,6 +24,7 @@ model Client { bookings Booking[] invoices Invoice[] messages Message[] @relation("ClientMessages") + pricing ClientPricing[] @@map("clients") } @@ -27,69 +37,69 @@ model Pet { species String breed String? age Int? - weight String? // e.g. "55 lbs" + weight String? color String? tags String[] @default([]) notes String? @default("") customFields Json? clientId String client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) + bookings Booking[] @relation("PetBookings") @@map("pets") } model Service { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - name String - description String @default("") - duration Int // minutes - basePrice Float // dollars + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String + description String @default("") + duration Int + basePrice Float perClientPricing ClientPricing[] + bookings Booking[] @@map("services") } model ClientPricing { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - price Float - discountType String @default("none") // none, percentage, flat - discountValue Float @default(0) + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + price Float + discountType String @default("none") + discountValue Float @default(0) serviceId String - service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade) + service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade) clientId String - client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) + client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) @@unique([serviceId, clientId]) @@map("client_pricing") } model Booking { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - startAt DateTime - endAt DateTime - status BookingStatus @default(scheduled) - notes String? @default("") - customFields Json? - - clientId String - client Client @relation(fields: [clientId], references: [id]) - serviceId String - service Service @relation(fields: [serviceId], references: [id]) + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + startAt DateTime + endAt DateTime + status BookingStatus @default(scheduled) + notes String? @default("") + customFields Json? + clientId String + client Client @relation(fields: [clientId], references: [id]) + serviceId String + service Service @relation(fields: [serviceId], references: [id]) assignedStaff String? priceOverride Float? - petId String? - pet Pet? @relation(fields: [petId], references: [id]) - invoice Invoice? - - recurringRule Json? // { interval: "weekly" | "biweekly" | "monthly", count: Int, endDate: DateTime? } + petId String? + pet Pet? @relation("PetBookings", fields: [petId], references: [id]) + invoice Invoice? @relation(references: [id]) + recurringRule Json? @@map("bookings") } @@ -103,24 +113,23 @@ enum BookingStatus { } model Invoice { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - invoiceNumber String @unique - dueDate DateTime - status InvoiceStatus @default(draft) + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + invoiceNumber String @unique + dueDate DateTime + status InvoiceStatus @default(draft) stripePaymentIntentId String? - subtotal Float @default(0) - tax Float @default(0) - total Float @default(0) - notes String? @default("") + subtotal Float @default(0) + tax Float @default(0) + total Float @default(0) + notes String? @default("") clientId String client Client @relation(fields: [clientId], references: [id]) - bookingId String? - booking Booking? @relation(fields: [bookingId], references: [id]) - - items InvoiceItem[] + bookingId String? @unique + booking Booking? @relation(references: [id]) + invoiceItems InvoiceItem[] @@map("invoices") } @@ -148,15 +157,15 @@ model InvoiceItem { } model Message { - id String @id @default(cuid()) - createdAt DateTime @default(now()) - readAt DateTime? - content String - isFromStaff Boolean @default(false) + id String @id @default(cuid()) + createdAt DateTime @default(now()) + readAt DateTime? + content String + isFromStaff Boolean @default(false) clientId String client Client @relation("ClientMessages", fields: [clientId], references: [id]) - staffId String // staff member identifier + staffId String @@map("messages") } diff --git a/api/src/main.ts b/api/src/main.ts index 3c76ad6..fc76bb4 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -24,7 +24,7 @@ async function bootstrap() { SwaggerModule.setup('api/docs', app, document); const port = process.env.PORT || 3000; - await app.listen(port); - console.log(`API running on http://localhost:${port}`); + await app.listen(port, '0.0.0.0'); + console.log(`API running on http://0.0.0.0:${port}`); } bootstrap(); diff --git a/docker-compose.yml b/docker-compose.yml index 047ed28..c990a28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: JWT_SECRET: ${JWT_SECRET:-change-me-in-production} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-sk_test_placeholder} STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-whsec_placeholder} - CORS_ORIGIN: http://localhost:3001 + CORS_ORIGIN: "*" depends_on: postgres: condition: service_healthy @@ -70,27 +70,12 @@ services: ports: - "3001:3001" environment: - REACT_APP_API_URL: http://localhost:3000 + REACT_APP_API_URL: http://api:3000 depends_on: - api networks: - petmaster-net - nginx: - image: nginx:alpine - container_name: petmaster-nginx - restart: unless-stopped - ports: - - "80:80" - - "443:443" - volumes: - - ./web/nginx.conf:/etc/nginx/nginx.conf - depends_on: - - web - - api - networks: - - petmaster-net - volumes: pgdata: driver: local diff --git a/web/Dockerfile b/web/Dockerfile index 0eac740..4e87d34 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -9,6 +9,18 @@ RUN yarn install --frozen-lockfile 2>/dev/null || npm install COPY . . RUN npm run build +FROM node:22-alpine AS dev + +WORKDIR /app + +COPY package.json yarn.lock* ./ +RUN yarn install + +COPY . . + +EXPOSE 3001 +CMD ["npm", "run", "start", "--", "--port", "3001"] + FROM nginx:alpine COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf