Compare commits
32 Commits
main
...
implement-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9133950d6a | ||
|
|
e2299c6d46 | ||
|
|
62d28734fc | ||
|
|
b7c303377a | ||
|
|
1d09716492 | ||
|
|
c409d44765 | ||
|
|
2b95ab37f6 | ||
|
|
ff42f3cd60 | ||
|
|
43df46f520 | ||
|
|
70273f6760 | ||
|
|
566291fb43 | ||
|
|
91dfdce66d | ||
|
|
8210a4bd9b | ||
|
|
839e316226 | ||
|
|
c2f5c0d97c | ||
|
|
1911d5623c | ||
|
|
9ee98a4cb4 | ||
|
|
77004b14b0 | ||
|
|
2d86881f94 | ||
|
|
4dcaae7136 | ||
|
|
3cba2fa485 | ||
|
|
ae90562d88 | ||
|
|
7ca74970fa | ||
|
|
aa2fe41ecc | ||
|
|
11dd12682c | ||
|
|
e58fa6efa2 | ||
|
|
9f14b74936 | ||
|
|
063991770a | ||
|
|
2a0558347a | ||
|
|
58c5ce339b | ||
|
|
30ac0d569a | ||
|
|
8a01606c80 |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
bun.lock
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
# ---> Node
|
# ---> Node
|
||||||
# Logs
|
# Logs
|
||||||
@@ -10,6 +9,7 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
.vite
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
@@ -139,3 +139,20 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Bun
|
||||||
|
bun.lock
|
||||||
|
|
||||||
|
# Generated
|
||||||
|
apps/backend/src/generated
|
||||||
|
apps/backend/prisma.config.js
|
||||||
|
apps/backend/prisma/*.js
|
||||||
|
apps/backend/prisma/*.seed-data.js
|
||||||
|
apps/backend/src/app.js
|
||||||
|
apps/backend/src/lib/*.js
|
||||||
|
apps/backend/src/middlewares/*.js
|
||||||
|
apps/backend/src/modules/**/*.js
|
||||||
|
apps/backend/src/register-routes.js
|
||||||
|
apps/backend/src/server.js
|
||||||
|
apps/backend/src/index.js
|
||||||
|
apps/backend/src/types/*.js
|
||||||
|
|
||||||
|
|||||||
67
AGENTS.md
Normal file
67
AGENTS.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Root (runs both frontend + backend)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# Individual packages
|
||||||
|
bun run dev:frontend
|
||||||
|
bun run dev:backend
|
||||||
|
bun run build:frontend
|
||||||
|
|
||||||
|
# Linting (Biome - ESLint was replaced)
|
||||||
|
bun run lint
|
||||||
|
bun run lint:fix # fixes and formats
|
||||||
|
|
||||||
|
# Prisma (run from backend)
|
||||||
|
bun --filter backend prisma:generate # regenerate client after schema changes
|
||||||
|
bun --filter backend prisma:migrate # apply migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/frontend/ # React + Vite + TanStack Router/Query
|
||||||
|
apps/backend/ # Bun + Hono + Prisma
|
||||||
|
packages/api-contract/ # Shared Zod schemas, types, route definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Shared contract pattern:**
|
||||||
|
1. Update `packages/api-contract` first (schemas/types)
|
||||||
|
2. Implement handler in `apps/backend`
|
||||||
|
3. Consume from `apps/frontend` via workspace import `@repo/api-contract`
|
||||||
|
|
||||||
|
## Key Quirks
|
||||||
|
|
||||||
|
- **Prisma 7**: Uses `prisma.config.ts`, requires `DATABASE_URL` env var at generate time
|
||||||
|
- **Zod v4**: Use `.issues` instead of `.errors` for validation errors
|
||||||
|
- **Generated files**: `routeTree.gen.ts` and `tsconfig*.json` are auto-generated (ignored by Biome)
|
||||||
|
- **TanStack Router**: Routes are code-generated. Use `--filter frontend build` not raw vite build.
|
||||||
|
|
||||||
|
## Docker Deployment (Dokploy)
|
||||||
|
|
||||||
|
- **Build Context**: `./` (monorepo root)
|
||||||
|
- **Dockerfile**: `Dockerfile` (in root, not `apps/backend/`)
|
||||||
|
- **Required args**: `DATABASE_URL`, `VITE_API_BASE_URL`, `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY`
|
||||||
|
- **bun.lock must be tracked**: It's in `.gitignore` by default - remove it for Docker builds
|
||||||
|
|
||||||
|
## Linting
|
||||||
|
|
||||||
|
Biome is used (not ESLint). Config in `biome.json`:
|
||||||
|
- Line width: 100
|
||||||
|
- Quote style: single
|
||||||
|
- Semicolons: always
|
||||||
|
- `noUnusedVariables`: warn
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
**Backend** (`apps/backend/.env`):
|
||||||
|
- `DATABASE_URL` - PostgreSQL connection (required for Prisma)
|
||||||
|
- `SUPABASE_URL`, `SUPABASE_ANON_KEY` - Auth
|
||||||
|
- `CORS_ORIGIN` - Frontend URL
|
||||||
|
|
||||||
|
**Frontend** (`apps/frontend/.env`):
|
||||||
|
- `VITE_*` prefix required (Vite embeds these at build time)
|
||||||
|
- `VITE_API_BASE_URL` - Backend URL (default: http://localhost:3000)
|
||||||
73
Dockerfile
Normal file
73
Dockerfile
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 1: Install frontend dependencies
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS deps-frontend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
COPY apps/frontend/package.json apps/frontend/
|
||||||
|
COPY packages/api-contract/package.json packages/api-contract/
|
||||||
|
RUN bun install
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 2: Build Frontend
|
||||||
|
# =============================================================================
|
||||||
|
FROM deps-frontend AS build-frontend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY apps/frontend ./apps/frontend
|
||||||
|
COPY packages ./packages
|
||||||
|
ARG VITE_API_BASE_URL
|
||||||
|
ARG VITE_SUPABASE_URL
|
||||||
|
ARG VITE_SUPABASE_ANON_KEY
|
||||||
|
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
||||||
|
ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
|
||||||
|
ENV VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
|
||||||
|
RUN bun run build:frontend
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 3: Install backend dependencies
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS deps-backend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
COPY apps/backend/package.json apps/backend/
|
||||||
|
COPY packages/api-contract/package.json packages/api-contract/
|
||||||
|
RUN bun install
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 4: Prisma Generate
|
||||||
|
# =============================================================================
|
||||||
|
FROM deps-backend AS prisma
|
||||||
|
WORKDIR /app
|
||||||
|
ARG DATABASE_URL
|
||||||
|
ENV DATABASE_URL=${DATABASE_URL}
|
||||||
|
COPY apps/backend ./apps/backend
|
||||||
|
RUN bun --cwd apps/backend prisma:generate
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 5: Build Backend
|
||||||
|
# =============================================================================
|
||||||
|
FROM prisma AS build-backend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY packages ./packages
|
||||||
|
RUN bun --cwd apps/backend build
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 6: Runner - Production image
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
|
||||||
|
|
||||||
|
COPY --from=build-backend /app/node_modules ./node_modules
|
||||||
|
COPY --from=build-frontend /app/apps/frontend/dist ./apps/backend/public
|
||||||
|
COPY --from=build-backend /app/apps/backend ./apps/backend
|
||||||
|
COPY --from=build-backend /app/packages ./packages
|
||||||
|
COPY package.json ./
|
||||||
|
COPY entrypoint.sh /app/entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["/app/entrypoint.sh"]
|
||||||
@@ -3,6 +3,8 @@ SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhY
|
|||||||
SUPABASE_ANON_KEY=sb_publishable_RgMLVBTCTClhK-1Ks_X02Q_Sq1Z1Pq-
|
SUPABASE_ANON_KEY=sb_publishable_RgMLVBTCTClhK-1Ks_X02Q_Sq1Z1Pq-
|
||||||
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
|
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
|
||||||
DATABASE_URL=postgres://super_admin:solotech25@localhost:5432/playzer-dev
|
DATABASE_URL=postgres://super_admin:solotech25@localhost:5432/playzer-dev
|
||||||
|
BETTER_AUTH_SECRET=767a2e0fb8b58a9f4df3155d88d22ecdc4eaea4447565c2ed518c532f6410598
|
||||||
|
BETTER_AUTH_URL=http://localhost:5173
|
||||||
APP_BASE_URL=http://localhost:5173
|
APP_BASE_URL=http://localhost:5173
|
||||||
ONBOARDING_TTL_MINUTES=60
|
ONBOARDING_TTL_MINUTES=60
|
||||||
SMTP_HOST=sandbox.smtp.mailtrap.io
|
SMTP_HOST=sandbox.smtp.mailtrap.io
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
|
||||||
|
|
||||||
FROM oven/bun:1.3 AS deps
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Workspace manifests first for better layer caching.
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
COPY apps/backend/package.json apps/backend/package.json
|
|
||||||
COPY packages/api-contract/package.json packages/api-contract/package.json
|
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM deps AS build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY apps/backend apps/backend
|
|
||||||
COPY packages/api-contract packages/api-contract
|
|
||||||
|
|
||||||
# Prisma 7 config resolves DATABASE_URL at generate time.
|
|
||||||
ARG DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
|
||||||
ENV DATABASE_URL=${DATABASE_URL}
|
|
||||||
|
|
||||||
RUN bun --cwd apps/backend run prisma:generate
|
|
||||||
|
|
||||||
FROM oven/bun:1.3-slim AS runner
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
ENV HOST=0.0.0.0
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
COPY --from=build /app/apps/backend ./apps/backend
|
|
||||||
COPY --from=build /app/packages ./packages
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
|
|
||||||
WORKDIR /app/apps/backend
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["bun", "run", "start"]
|
|
||||||
26
apps/backend/bun.lock
Normal file
26
apps/backend/bun.lock
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "backend",
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.12.9",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||||
|
|
||||||
|
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,17 +3,26 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --hot src/server.ts",
|
"dev": "bun run --hot src/server.ts",
|
||||||
"start": "bun src/server.ts",
|
"start": "bun src/server.ts",
|
||||||
|
"build": "tsc -b",
|
||||||
|
"lint": "biome check .",
|
||||||
|
"lint:fix": "biome check --write .",
|
||||||
|
"format": "biome format --write .",
|
||||||
"prisma:generate": "prisma generate",
|
"prisma:generate": "prisma generate",
|
||||||
"prisma:migrate": "prisma migrate dev",
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:migrate:deploy": "prisma migrate deploy",
|
||||||
"prisma:seed": "prisma db seed",
|
"prisma:seed": "prisma db seed",
|
||||||
"prisma:seed:reset:plans": "bun prisma/reset-plans.ts"
|
"prisma:seed:reset:plans": "bun prisma/reset-plans.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@better-auth/prisma-adapter": "^1.6.2",
|
||||||
"@hono/zod-validator": "^0.7.6",
|
"@hono/zod-validator": "^0.7.6",
|
||||||
"@prisma/adapter-pg": "^7.6.0",
|
"@prisma/adapter-pg": "^7.6.0",
|
||||||
"@prisma/client": "^7",
|
"@prisma/client": "^7",
|
||||||
"@repo/api-contract": "workspace:*",
|
"@repo/api-contract": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.101.1",
|
"@supabase/supabase-js": "2.101.1",
|
||||||
|
"@types/bcrypt": "^6.0.0",
|
||||||
|
"bcrypt": "^6.0.0",
|
||||||
|
"better-auth": "^1.6.2",
|
||||||
"dotenv": "^17.4.1",
|
"dotenv": "^17.4.1",
|
||||||
"hono": "4.12.10",
|
"hono": "4.12.10",
|
||||||
"nodemailer": "^8.0.5",
|
"nodemailer": "^8.0.5",
|
||||||
@@ -21,11 +30,11 @@
|
|||||||
"pino": "10.3.1",
|
"pino": "10.3.1",
|
||||||
"pino-pretty": "13.1.3",
|
"pino-pretty": "13.1.3",
|
||||||
"pino-std-serializers": "7.1.0",
|
"pino-std-serializers": "7.1.0",
|
||||||
|
"prisma": "^7",
|
||||||
"uuid": "^13.0.0"
|
"uuid": "^13.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/nodemailer": "^8.0.0",
|
"@types/nodemailer": "^8.0.0"
|
||||||
"prisma": "^7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { defineConfig, env } from 'prisma/config'
|
import { defineConfig, env } from 'prisma/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
schema: 'prisma',
|
schema: 'prisma',
|
||||||
@@ -10,4 +10,4 @@ export default defineConfig({
|
|||||||
datasource: {
|
datasource: {
|
||||||
url: env('DATABASE_URL'),
|
url: env('DATABASE_URL'),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
model Complex {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
complexName String @map("complex_name")
|
|
||||||
physicalAddress String? @map("physical_address") @db.VarChar(200)
|
|
||||||
complexSlug String @unique @map("complex_slug")
|
|
||||||
adminEmail String @map("admin_email")
|
|
||||||
planCode String? @map("plan_code") @db.VarChar(10)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
plan Plan? @relation(fields: [planCode], references: [code])
|
|
||||||
users ComplexUser[]
|
|
||||||
courts Court[]
|
|
||||||
|
|
||||||
@@index([planCode])
|
|
||||||
@@index([complexSlug])
|
|
||||||
@@map("complexes")
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ComplexUserRole {
|
|
||||||
ADMIN
|
|
||||||
}
|
|
||||||
|
|
||||||
model ComplexUser {
|
|
||||||
complexId String @map("complex_id") @db.Uuid
|
|
||||||
userId String @map("user_id") @db.Uuid
|
|
||||||
role ComplexUserRole @default(ADMIN)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@id([complexId, userId])
|
|
||||||
@@index([userId])
|
|
||||||
@@map("complex_users")
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
enum DayOfWeek {
|
|
||||||
MONDAY
|
|
||||||
TUESDAY
|
|
||||||
WEDNESDAY
|
|
||||||
THURSDAY
|
|
||||||
FRIDAY
|
|
||||||
SATURDAY
|
|
||||||
SUNDAY
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CourtBookingStatus {
|
|
||||||
CONFIRMED
|
|
||||||
CANCELLED
|
|
||||||
}
|
|
||||||
|
|
||||||
model Sport {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
name String @unique
|
|
||||||
slug String @unique
|
|
||||||
isActive Boolean @default(true) @map("is_active")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
courts Court[]
|
|
||||||
|
|
||||||
@@map("sports")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Court {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
complexId String @map("complex_id") @db.Uuid
|
|
||||||
sportId String @map("sport_id") @db.Uuid
|
|
||||||
name String
|
|
||||||
slotDurationMinutes Int @map("slot_duration_minutes")
|
|
||||||
basePrice Decimal @map("base_price") @db.Decimal(19, 2)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
|
||||||
sport Sport @relation(fields: [sportId], references: [id])
|
|
||||||
availabilities CourtAvailability[]
|
|
||||||
priceRules CourtPriceRule[]
|
|
||||||
bookings CourtBooking[]
|
|
||||||
|
|
||||||
@@index([complexId])
|
|
||||||
@@index([sportId])
|
|
||||||
@@map("courts")
|
|
||||||
}
|
|
||||||
|
|
||||||
model CourtAvailability {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
courtId String @map("court_id") @db.Uuid
|
|
||||||
dayOfWeek DayOfWeek @map("day_of_week")
|
|
||||||
startTime String @map("start_time") @db.VarChar(5)
|
|
||||||
endTime String @map("end_time") @db.VarChar(5)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@index([courtId, dayOfWeek])
|
|
||||||
@@map("court_availabilities")
|
|
||||||
}
|
|
||||||
|
|
||||||
model CourtPriceRule {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
courtId String @map("court_id") @db.Uuid
|
|
||||||
dayOfWeek DayOfWeek? @map("day_of_week")
|
|
||||||
startTime String? @map("start_time") @db.VarChar(5)
|
|
||||||
endTime String? @map("end_time") @db.VarChar(5)
|
|
||||||
price Decimal @db.Decimal(19, 2)
|
|
||||||
isActive Boolean @default(true) @map("is_active")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@index([courtId, isActive])
|
|
||||||
@@map("court_price_rules")
|
|
||||||
}
|
|
||||||
|
|
||||||
model CourtBooking {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
bookingCode String @unique @map("booking_code") @db.VarChar(8)
|
|
||||||
courtId String @map("court_id") @db.Uuid
|
|
||||||
bookingDate DateTime @map("booking_date") @db.Date
|
|
||||||
startTime String @map("start_time") @db.VarChar(5)
|
|
||||||
endTime String @map("end_time") @db.VarChar(5)
|
|
||||||
customerName String @map("customer_name") @db.VarChar(120)
|
|
||||||
customerPhone String @map("customer_phone") @db.VarChar(30)
|
|
||||||
status CourtBookingStatus @default(CONFIRMED)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@unique([courtId, bookingDate, startTime])
|
|
||||||
@@index([courtId, bookingDate])
|
|
||||||
@@index([bookingDate])
|
|
||||||
@@map("court_bookings")
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TYPE "CourtBookingStatus" ADD VALUE 'COMPLETED';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "complexes" ADD COLUMN "city" VARCHAR(100),
|
||||||
|
ADD COLUMN "country" VARCHAR(100),
|
||||||
|
ADD COLUMN "state" VARCHAR(100);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "password_reset_requests" (
|
||||||
|
"id" UUID NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"otp_hash" TEXT NOT NULL,
|
||||||
|
"otp_expires_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"otp_attempts" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"otp_last_sent_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"otp_resend_count" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"used_at" TIMESTAMP(3),
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "password_reset_requests_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "password_reset_requests_email_idx" ON "password_reset_requests"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "password_reset_requests_otp_expires_at_idx" ON "password_reset_requests"("otp_expires_at");
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
model OnboardingRequest {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
fullName String @map("full_name")
|
|
||||||
email String
|
|
||||||
otpHash String @map("otp_hash")
|
|
||||||
otpExpiresAt DateTime @map("otp_expires_at")
|
|
||||||
otpAttempts Int @default(0) @map("otp_attempts")
|
|
||||||
otpLastSentAt DateTime @map("otp_last_sent_at")
|
|
||||||
otpResendCount Int @default(0) @map("otp_resend_count")
|
|
||||||
emailVerifiedAt DateTime? @map("email_verified_at")
|
|
||||||
completedAt DateTime? @map("completed_at")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
|
|
||||||
@@index([email])
|
|
||||||
@@index([otpExpiresAt])
|
|
||||||
@@map("onboarding_requests")
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
model Plan {
|
|
||||||
code String @id @db.VarChar(10)
|
|
||||||
name String @db.VarChar(30)
|
|
||||||
price Decimal @db.Decimal(19, 2)
|
|
||||||
rules Json
|
|
||||||
lastUpdatedAt DateTime @default(now()) @map("last_updated_at")
|
|
||||||
complexes Complex[]
|
|
||||||
|
|
||||||
@@map("plans")
|
|
||||||
}
|
|
||||||
@@ -74,4 +74,4 @@ export const planSeeds = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as const
|
] as const;
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
import { PrismaClient } from '../src/generated/prisma/client'
|
import { PrismaClient } from '../src/generated/prisma/client';
|
||||||
import { planSeeds } from './plans.seed-data'
|
import { planSeeds } from './plans.seed-data';
|
||||||
|
|
||||||
const databaseUrl = process.env.DATABASE_URL
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in environment for plan reset.')
|
throw new Error('Missing DATABASE_URL in environment for plan reset.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
const prisma = new PrismaClient({ adapter })
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
const complexesUsingPlans = await prisma.complex.count({
|
const complexesUsingPlans = await prisma.complex.count({
|
||||||
where: { planCode: { not: null } },
|
where: { planCode: { not: null } },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (complexesUsingPlans > 0) {
|
if (complexesUsingPlans > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No se puede resetear planes: hay ${complexesUsingPlans} complejos con plan asignado.`,
|
`No se puede resetear planes: hay ${complexesUsingPlans} complejos con plan asignado.`
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.$transaction(async (tx) => {
|
await prisma.$transaction(async (tx) => {
|
||||||
await tx.plan.deleteMany({})
|
await tx.plan.deleteMany({});
|
||||||
|
|
||||||
for (const plan of planSeeds) {
|
for (const plan of planSeeds) {
|
||||||
await tx.plan.create({
|
await tx.plan.create({
|
||||||
@@ -37,19 +37,19 @@ async function run() {
|
|||||||
price: plan.price,
|
price: plan.price,
|
||||||
rules: plan.rules,
|
rules: plan.rules,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
console.log('Planes reseteados: BASIC, ADVANCED, ENTERPRISE')
|
console.log('Planes reseteados: BASIC, ADVANCED, ENTERPRISE');
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
})
|
})
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
process.exit(1)
|
process.exit(1);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -6,3 +6,253 @@ generator client {
|
|||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============== APP MODELS (existing) ==============
|
||||||
|
|
||||||
|
model Plan {
|
||||||
|
code String @id @map("code") @db.VarChar(10)
|
||||||
|
name String @db.VarChar(30)
|
||||||
|
price Decimal @map("price") @db.Decimal(19, 2)
|
||||||
|
rules Json @map("rules")
|
||||||
|
lastUpdatedAt DateTime @default(now()) @map("last_updated_at")
|
||||||
|
complexes Complex[]
|
||||||
|
|
||||||
|
@@map("plans")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Complex {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
complexSlug String @unique @map("complex_slug")
|
||||||
|
complexName String @map("complex_name")
|
||||||
|
physicalAddress String @map("physical_address")
|
||||||
|
city String?
|
||||||
|
state String?
|
||||||
|
country String?
|
||||||
|
adminEmail String @map("admin_email")
|
||||||
|
planCode String @map("plan_code")
|
||||||
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
plan Plan @relation(fields: [planCode], references: [code])
|
||||||
|
users ComplexUser[]
|
||||||
|
courts Court[]
|
||||||
|
|
||||||
|
@@index([adminEmail])
|
||||||
|
@@map("complexes")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ComplexUser {
|
||||||
|
id String @id @default(cuid()) @map("id")
|
||||||
|
complexId String @map("complex_id") @db.Uuid
|
||||||
|
userId String @map("user_id")
|
||||||
|
role ComplexUserRole
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([complexId, userId])
|
||||||
|
@@map("complex_users")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ComplexUserRole {
|
||||||
|
ADMIN
|
||||||
|
MANAGER
|
||||||
|
USER
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DayOfWeek {
|
||||||
|
MONDAY
|
||||||
|
TUESDAY
|
||||||
|
WEDNESDAY
|
||||||
|
THURSDAY
|
||||||
|
FRIDAY
|
||||||
|
SATURDAY
|
||||||
|
SUNDAY
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CourtBookingStatus {
|
||||||
|
CONFIRMED
|
||||||
|
CANCELLED
|
||||||
|
COMPLETED
|
||||||
|
}
|
||||||
|
|
||||||
|
model Sport {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
name String @unique
|
||||||
|
slug String @unique
|
||||||
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
courts Court[]
|
||||||
|
|
||||||
|
@@map("sports")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Court {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
complexId String @map("complex_id") @db.Uuid
|
||||||
|
sportId String @map("sport_id") @db.Uuid
|
||||||
|
name String
|
||||||
|
slotDurationMinutes Int @map("slot_duration_minutes")
|
||||||
|
basePrice Decimal @map("base_price") @db.Decimal(19, 2)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
||||||
|
sport Sport @relation(fields: [sportId], references: [id])
|
||||||
|
availabilities CourtAvailability[]
|
||||||
|
priceRules CourtPriceRule[]
|
||||||
|
bookings CourtBooking[]
|
||||||
|
|
||||||
|
@@index([complexId])
|
||||||
|
@@index([sportId])
|
||||||
|
@@map("courts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model CourtAvailability {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
courtId String @map("court_id") @db.Uuid
|
||||||
|
dayOfWeek DayOfWeek @map("day_of_week")
|
||||||
|
startTime String @map("start_time") @db.VarChar(5)
|
||||||
|
endTime String @map("end_time") @db.VarChar(5)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([courtId, dayOfWeek])
|
||||||
|
@@map("court_availabilities")
|
||||||
|
}
|
||||||
|
|
||||||
|
model CourtPriceRule {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
courtId String @map("court_id") @db.Uuid
|
||||||
|
dayOfWeek DayOfWeek? @map("day_of_week")
|
||||||
|
startTime String? @map("start_time") @db.VarChar(5)
|
||||||
|
endTime String? @map("end_time") @db.VarChar(5)
|
||||||
|
price Decimal @db.Decimal(19, 2)
|
||||||
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([courtId, isActive])
|
||||||
|
@@map("court_price_rules")
|
||||||
|
}
|
||||||
|
|
||||||
|
model CourtBooking {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
bookingCode String @unique @map("booking_code") @db.VarChar(8)
|
||||||
|
courtId String @map("court_id") @db.Uuid
|
||||||
|
bookingDate DateTime @map("booking_date") @db.Date
|
||||||
|
startTime String @map("start_time") @db.VarChar(5)
|
||||||
|
endTime String @map("end_time") @db.VarChar(5)
|
||||||
|
customerName String @map("customer_name") @db.VarChar(120)
|
||||||
|
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||||
|
status CourtBookingStatus @default(CONFIRMED)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([courtId, bookingDate, startTime])
|
||||||
|
@@index([courtId, bookingDate])
|
||||||
|
@@index([bookingDate])
|
||||||
|
@@map("court_bookings")
|
||||||
|
}
|
||||||
|
|
||||||
|
model OnboardingRequest {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
fullName String @map("full_name")
|
||||||
|
email String
|
||||||
|
otpHash String @map("otp_hash")
|
||||||
|
otpExpiresAt DateTime @map("otp_expires_at")
|
||||||
|
otpAttempts Int @default(0) @map("otp_attempts")
|
||||||
|
otpLastSentAt DateTime @map("otp_last_sent_at")
|
||||||
|
otpResendCount Int @default(0) @map("otp_resend_count")
|
||||||
|
emailVerifiedAt DateTime? @map("email_verified_at")
|
||||||
|
completedAt DateTime? @map("completed_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@index([email])
|
||||||
|
@@index([otpExpiresAt])
|
||||||
|
@@map("onboarding_requests")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PasswordResetRequest {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
email String
|
||||||
|
otpHash String @map("otp_hash")
|
||||||
|
otpExpiresAt DateTime @map("otp_expires_at")
|
||||||
|
otpAttempts Int @default(0) @map("otp_attempts")
|
||||||
|
otpLastSentAt DateTime @map("otp_last_sent_at")
|
||||||
|
otpResendCount Int @default(0) @map("otp_resend_count")
|
||||||
|
usedAt DateTime? @map("used_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@index([email])
|
||||||
|
@@index([otpExpiresAt])
|
||||||
|
@@map("password_reset_requests")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============== BETTER AUTH MODELS ==============
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid()) @map("id")
|
||||||
|
supabaseUserId String? @unique @map("supabase_user_id") @db.Uuid
|
||||||
|
name String?
|
||||||
|
email String @unique
|
||||||
|
emailVerified Boolean @default(false)
|
||||||
|
image String?
|
||||||
|
fullName String? @map("full_name")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
accounts Account[]
|
||||||
|
sessions Session[]
|
||||||
|
complexes ComplexUser[]
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Session {
|
||||||
|
id String @id @default(cuid()) @map("id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
token String @unique
|
||||||
|
expiresAt DateTime @map("expires_at")
|
||||||
|
ipAddress String? @map("ip_address")
|
||||||
|
userAgent String? @map("user_agent")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("session")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Account {
|
||||||
|
id String @id @default(cuid()) @map("id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
accountId String @map("account_id")
|
||||||
|
providerId String @map("provider_id")
|
||||||
|
accessToken String? @map("access_token")
|
||||||
|
refreshToken String? @map("refresh_token")
|
||||||
|
accessTokenExpiresAt DateTime? @map("access_token_expires_at")
|
||||||
|
refreshTokenExpiresAt DateTime? @map("refresh_token_expires_at")
|
||||||
|
scope String? @map("scope")
|
||||||
|
idToken String? @map("id_token")
|
||||||
|
password String? @map("password")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("account")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Verification {
|
||||||
|
id String @id @default(cuid()) @map("id")
|
||||||
|
identifier String
|
||||||
|
value String
|
||||||
|
expiresAt DateTime @map("expires_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("verification")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
import { v7 as uuidv7 } from 'uuid'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
import { PrismaClient } from '../src/generated/prisma/client'
|
import { PrismaClient } from '../src/generated/prisma/client';
|
||||||
import { planSeeds } from './plans.seed-data'
|
import { planSeeds } from './plans.seed-data';
|
||||||
import { sportSeeds } from './sports.seed-data'
|
import { sportSeeds } from './sports.seed-data';
|
||||||
|
|
||||||
const databaseUrl = process.env.DATABASE_URL
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in environment for seeding.')
|
throw new Error('Missing DATABASE_URL in environment for seeding.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
const prisma = new PrismaClient({ adapter })
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
for (const plan of planSeeds) {
|
for (const plan of planSeeds) {
|
||||||
@@ -33,7 +33,7 @@ async function run() {
|
|||||||
price: plan.price,
|
price: plan.price,
|
||||||
rules: plan.rules,
|
rules: plan.rules,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const sport of sportSeeds) {
|
for (const sport of sportSeeds) {
|
||||||
@@ -49,16 +49,16 @@ async function run() {
|
|||||||
slug: sport.slug,
|
slug: sport.slug,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
})
|
})
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
process.exit(1)
|
process.exit(1);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ export const sportSeeds = [
|
|||||||
{ name: 'Pickleball', slug: 'pickleball' },
|
{ name: 'Pickleball', slug: 'pickleball' },
|
||||||
{ name: 'Basquet', slug: 'basquet' },
|
{ name: 'Basquet', slug: 'basquet' },
|
||||||
{ name: 'Basquet 3', slug: 'basquet-3' },
|
{ name: 'Basquet 3', slug: 'basquet-3' },
|
||||||
] as const
|
] as const;
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
model User {
|
|
||||||
id String @id @db.Uuid
|
|
||||||
supabaseUserId String @unique @map("supabase_user_id") @db.Uuid
|
|
||||||
email String @unique
|
|
||||||
fullName String @map("full_name")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
complexes ComplexUser[]
|
|
||||||
|
|
||||||
@@map("users")
|
|
||||||
}
|
|
||||||
@@ -1,36 +1,34 @@
|
|||||||
import { Hono } from 'hono'
|
import { logger } from '@/lib/logger';
|
||||||
import { cors } from 'hono/cors'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { logger } from '@/lib/logger'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { cors } from 'hono/cors';
|
||||||
import { requestId } from 'hono/request-id'
|
import { requestId } from 'hono/request-id';
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = new Hono<AppEnv>()
|
const app = new Hono<AppEnv>();
|
||||||
|
|
||||||
const allowedOrigins = (
|
const allowedOrigins = (Bun.env.CORS_ORIGIN ?? 'http://localhost:5173,http://127.0.0.1:5173')
|
||||||
Bun.env.CORS_ORIGIN ??
|
|
||||||
'http://localhost:5173,http://127.0.0.1:5173'
|
|
||||||
)
|
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((value) => value.trim())
|
.map((value) => value.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'*',
|
'*',
|
||||||
cors({
|
cors({
|
||||||
origin: (origin) => {
|
origin: (origin) => {
|
||||||
if (!origin) return allowedOrigins[0] ?? ''
|
if (!origin) return allowedOrigins[0] ?? '';
|
||||||
return allowedOrigins.includes(origin) ? origin : ''
|
return allowedOrigins.includes(origin) ? origin : '';
|
||||||
},
|
},
|
||||||
allowHeaders: ['Authorization', 'Content-Type'],
|
allowHeaders: ['Authorization', 'Content-Type'],
|
||||||
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
}),
|
credentials: true,
|
||||||
)
|
})
|
||||||
|
);
|
||||||
|
|
||||||
app.use('*', async (c, next) => {
|
app.use('*', async (c, next) => {
|
||||||
const start = performance.now()
|
const start = performance.now();
|
||||||
await next()
|
await next();
|
||||||
const durationMs = Number((performance.now() - start).toFixed(1))
|
const durationMs = Number((performance.now() - start).toFixed(1));
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
{
|
{
|
||||||
@@ -38,11 +36,11 @@ export function createApp() {
|
|||||||
path: c.req.path,
|
path: c.req.path,
|
||||||
status: c.res.status,
|
status: c.res.status,
|
||||||
durationMs,
|
durationMs,
|
||||||
requestId
|
requestId,
|
||||||
},
|
},
|
||||||
'http_request',
|
'http_request'
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return app
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
21
apps/backend/src/auth-routes.js
Normal file
21
apps/backend/src/auth-routes.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { auth } from '@/lib/auth';
|
||||||
|
import { cors } from 'hono/cors';
|
||||||
|
export function registerAuthRoutes(app) {
|
||||||
|
const allowedOrigins = (Bun.env.CORS_ORIGIN ?? 'http://localhost:5173,http://127.0.0.1:5173')
|
||||||
|
.split(',')
|
||||||
|
.map((value) => value.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
app.use('/api/auth/*', cors({
|
||||||
|
origin: (origin) => {
|
||||||
|
if (!origin)
|
||||||
|
return allowedOrigins[0] ?? '';
|
||||||
|
return allowedOrigins.includes(origin) ? origin : '';
|
||||||
|
},
|
||||||
|
allowHeaders: ['Content-Type', 'Authorization'],
|
||||||
|
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
|
credentials: true,
|
||||||
|
}));
|
||||||
|
app.on(['POST', 'GET'], '/api/auth/*', (c) => {
|
||||||
|
return auth.handler(c.req.raw);
|
||||||
|
});
|
||||||
|
}
|
||||||
28
apps/backend/src/auth-routes.ts
Normal file
28
apps/backend/src/auth-routes.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { auth } from '@/lib/auth';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import type { Hono } from 'hono';
|
||||||
|
import { cors } from 'hono/cors';
|
||||||
|
|
||||||
|
export function registerAuthRoutes(app: Hono<AppEnv>) {
|
||||||
|
const allowedOrigins = (Bun.env.CORS_ORIGIN ?? 'http://localhost:5173,http://127.0.0.1:5173')
|
||||||
|
.split(',')
|
||||||
|
.map((value) => value.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/api/auth/*',
|
||||||
|
cors({
|
||||||
|
origin: (origin) => {
|
||||||
|
if (!origin) return allowedOrigins[0] ?? '';
|
||||||
|
return allowedOrigins.includes(origin) ? origin : '';
|
||||||
|
},
|
||||||
|
allowHeaders: ['Content-Type', 'Authorization'],
|
||||||
|
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
|
credentials: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.on(['POST', 'GET'], '/api/auth/*', (c) => {
|
||||||
|
return auth.handler(c.req.raw);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -17,6 +17,11 @@ import * as Prisma from './internal/prismaNamespaceBrowser'
|
|||||||
export { Prisma }
|
export { Prisma }
|
||||||
export * as $Enums from './enums'
|
export * as $Enums from './enums'
|
||||||
export * from './enums';
|
export * from './enums';
|
||||||
|
/**
|
||||||
|
* Model Plan
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Plan = Prisma.PlanModel
|
||||||
/**
|
/**
|
||||||
* Model Complex
|
* Model Complex
|
||||||
*
|
*
|
||||||
@@ -58,12 +63,27 @@ export type CourtBooking = Prisma.CourtBookingModel
|
|||||||
*/
|
*/
|
||||||
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
||||||
/**
|
/**
|
||||||
* Model Plan
|
* Model PasswordResetRequest
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Plan = Prisma.PlanModel
|
export type PasswordResetRequest = Prisma.PasswordResetRequestModel
|
||||||
/**
|
/**
|
||||||
* Model User
|
* Model User
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model Session
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Session = Prisma.SessionModel
|
||||||
|
/**
|
||||||
|
* Model Account
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Account = Prisma.AccountModel
|
||||||
|
/**
|
||||||
|
* Model Verification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Verification = Prisma.VerificationModel
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ export * from "./enums"
|
|||||||
* const prisma = new PrismaClient({
|
* const prisma = new PrismaClient({
|
||||||
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
||||||
* })
|
* })
|
||||||
* // Fetch zero or more Complexes
|
* // Fetch zero or more Plans
|
||||||
* const complexes = await prisma.complex.findMany()
|
* const plans = await prisma.plan.findMany()
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Read more in our [docs](https://pris.ly/d/client).
|
* Read more in our [docs](https://pris.ly/d/client).
|
||||||
@@ -41,6 +41,11 @@ export const PrismaClient = $Class.getPrismaClientClass()
|
|||||||
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||||
export { Prisma }
|
export { Prisma }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model Plan
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Plan = Prisma.PlanModel
|
||||||
/**
|
/**
|
||||||
* Model Complex
|
* Model Complex
|
||||||
*
|
*
|
||||||
@@ -82,12 +87,27 @@ export type CourtBooking = Prisma.CourtBookingModel
|
|||||||
*/
|
*/
|
||||||
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
||||||
/**
|
/**
|
||||||
* Model Plan
|
* Model PasswordResetRequest
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Plan = Prisma.PlanModel
|
export type PasswordResetRequest = Prisma.PasswordResetRequestModel
|
||||||
/**
|
/**
|
||||||
* Model User
|
* Model User
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model Session
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Session = Prisma.SessionModel
|
||||||
|
/**
|
||||||
|
* Model Account
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Account = Prisma.AccountModel
|
||||||
|
/**
|
||||||
|
* Model Verification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Verification = Prisma.VerificationModel
|
||||||
|
|||||||
@@ -14,18 +14,6 @@ import * as $Enums from "./enums"
|
|||||||
import type * as Prisma from "./internal/prismaNamespace"
|
import type * as Prisma from "./internal/prismaNamespace"
|
||||||
|
|
||||||
|
|
||||||
export type UuidFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
mode?: Prisma.QueryMode
|
|
||||||
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StringFilter<$PrismaModel = never> = {
|
export type StringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -41,6 +29,139 @@ export type StringFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DecimalFilter<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDecimalFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JsonFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<JsonFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type JsonFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DecimalWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDecimalWithAggregatesFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type JsonWithAggregatesFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UuidFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
export type StringNullableFilter<$PrismaModel = never> = {
|
export type StringNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
@@ -56,15 +177,9 @@ export type StringNullableFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DateTimeFilter<$PrismaModel = never> = {
|
export type BoolFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SortOrderInput = {
|
export type SortOrderInput = {
|
||||||
@@ -87,24 +202,6 @@ export type UuidWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
mode?: Prisma.QueryMode
|
|
||||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
@@ -123,18 +220,12 @@ export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EnumComplexUserRoleFilter<$PrismaModel = never> = {
|
export type EnumComplexUserRoleFilter<$PrismaModel = never> = {
|
||||||
@@ -154,19 +245,6 @@ export type EnumComplexUserRoleWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoolFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntFilter<$PrismaModel = never> = {
|
export type IntFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
@@ -178,17 +256,6 @@ export type IntFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DecimalFilter<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDecimalFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
@@ -205,22 +272,6 @@ export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DecimalWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDecimalWithAggregatesFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_avg?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_sum?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EnumDayOfWeekFilter<$PrismaModel = never> = {
|
export type EnumDayOfWeekFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.DayOfWeek | Prisma.EnumDayOfWeekFieldRefInput<$PrismaModel>
|
equals?: $Enums.DayOfWeek | Prisma.EnumDayOfWeekFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.DayOfWeek[] | Prisma.ListEnumDayOfWeekFieldRefInput<$PrismaModel>
|
in?: $Enums.DayOfWeek[] | Prisma.ListEnumDayOfWeekFieldRefInput<$PrismaModel>
|
||||||
@@ -297,66 +348,31 @@ export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JsonFilter<$PrismaModel = never> =
|
export type UuidNullableFilter<$PrismaModel = never> = {
|
||||||
| Prisma.PatchUndefined<
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
Required<JsonFilterBase<$PrismaModel>>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
>
|
|
||||||
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
|
|
||||||
|
|
||||||
export type JsonFilterBase<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
|
||||||
path?: string[]
|
|
||||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
|
||||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
export type JsonWithAggregatesFilter<$PrismaModel = never> =
|
|
||||||
| Prisma.PatchUndefined<
|
|
||||||
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
|
||||||
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
|
|
||||||
>
|
|
||||||
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
|
|
||||||
|
|
||||||
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
|
||||||
path?: string[]
|
|
||||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
|
||||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedJsonFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedUuidFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedUuidNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UuidNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedUuidNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedStringFilter<$PrismaModel = never> = {
|
export type NestedStringFilter<$PrismaModel = never> = {
|
||||||
@@ -373,18 +389,15 @@ export type NestedStringFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
export type NestedDecimalFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedDecimalFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||||
@@ -398,7 +411,7 @@ export type NestedDateTimeFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -406,7 +419,10 @@ export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
@@ -423,7 +439,61 @@ export type NestedIntFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedDecimalWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
|
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDecimalWithAggregatesFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDecimalFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedJsonFilter<$PrismaModel = never> =
|
||||||
|
| Prisma.PatchUndefined<
|
||||||
|
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
Required<NestedJsonFilterBase<$PrismaModel>>
|
||||||
|
>
|
||||||
|
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
|
||||||
|
|
||||||
|
export type NestedJsonFilterBase<$PrismaModel = never> = {
|
||||||
|
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
path?: string[]
|
||||||
|
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
||||||
|
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
||||||
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedUuidFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -431,10 +501,37 @@ export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
@@ -468,18 +565,12 @@ export type NestedIntNullableFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedEnumComplexUserRoleFilter<$PrismaModel = never> = {
|
export type NestedEnumComplexUserRoleFilter<$PrismaModel = never> = {
|
||||||
@@ -499,30 +590,6 @@ export type NestedEnumComplexUserRoleWithAggregatesFilter<$PrismaModel = never>
|
|||||||
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedBoolFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedDecimalFilter<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDecimalFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
@@ -550,22 +617,6 @@ export type NestedFloatFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
|
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDecimalWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
|
||||||
lt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
lte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gt?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDecimalWithAggregatesFilter<$PrismaModel> | runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_avg?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_sum?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDecimalFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedEnumDayOfWeekFilter<$PrismaModel = never> = {
|
export type NestedEnumDayOfWeekFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.DayOfWeek | Prisma.EnumDayOfWeekFieldRefInput<$PrismaModel>
|
equals?: $Enums.DayOfWeek | Prisma.EnumDayOfWeekFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.DayOfWeek[] | Prisma.ListEnumDayOfWeekFieldRefInput<$PrismaModel>
|
in?: $Enums.DayOfWeek[] | Prisma.ListEnumDayOfWeekFieldRefInput<$PrismaModel>
|
||||||
@@ -642,28 +693,29 @@ export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedJsonFilter<$PrismaModel = never> =
|
export type NestedUuidNullableFilter<$PrismaModel = never> = {
|
||||||
| Prisma.PatchUndefined<
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
Required<NestedJsonFilterBase<$PrismaModel>>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
>
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedUuidNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedJsonFilterBase<$PrismaModel = never> = {
|
export type NestedUuidNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
path?: string[]
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
not?: Prisma.NestedUuidNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
|
|
||||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const ComplexUserRole = {
|
export const ComplexUserRole = {
|
||||||
ADMIN: 'ADMIN'
|
ADMIN: 'ADMIN',
|
||||||
|
MANAGER: 'MANAGER',
|
||||||
|
USER: 'USER'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ComplexUserRole = (typeof ComplexUserRole)[keyof typeof ComplexUserRole]
|
export type ComplexUserRole = (typeof ComplexUserRole)[keyof typeof ComplexUserRole]
|
||||||
@@ -31,7 +33,8 @@ export type DayOfWeek = (typeof DayOfWeek)[keyof typeof DayOfWeek]
|
|||||||
|
|
||||||
export const CourtBookingStatus = {
|
export const CourtBookingStatus = {
|
||||||
CONFIRMED: 'CONFIRMED',
|
CONFIRMED: 'CONFIRMED',
|
||||||
CANCELLED: 'CANCELLED'
|
CANCELLED: 'CANCELLED',
|
||||||
|
COMPLETED: 'COMPLETED'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type CourtBookingStatus = (typeof CourtBookingStatus)[keyof typeof CourtBookingStatus]
|
export type CourtBookingStatus = (typeof CourtBookingStatus)[keyof typeof CourtBookingStatus]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -384,6 +384,7 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
|
|||||||
|
|
||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
|
Plan: 'Plan',
|
||||||
Complex: 'Complex',
|
Complex: 'Complex',
|
||||||
ComplexUser: 'ComplexUser',
|
ComplexUser: 'ComplexUser',
|
||||||
Sport: 'Sport',
|
Sport: 'Sport',
|
||||||
@@ -392,8 +393,11 @@ export const ModelName = {
|
|||||||
CourtPriceRule: 'CourtPriceRule',
|
CourtPriceRule: 'CourtPriceRule',
|
||||||
CourtBooking: 'CourtBooking',
|
CourtBooking: 'CourtBooking',
|
||||||
OnboardingRequest: 'OnboardingRequest',
|
OnboardingRequest: 'OnboardingRequest',
|
||||||
Plan: 'Plan',
|
PasswordResetRequest: 'PasswordResetRequest',
|
||||||
User: 'User'
|
User: 'User',
|
||||||
|
Session: 'Session',
|
||||||
|
Account: 'Account',
|
||||||
|
Verification: 'Verification'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -409,10 +413,84 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
omit: GlobalOmitOptions
|
omit: GlobalOmitOptions
|
||||||
}
|
}
|
||||||
meta: {
|
meta: {
|
||||||
modelProps: "complex" | "complexUser" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "onboardingRequest" | "plan" | "user"
|
modelProps: "plan" | "complex" | "complexUser" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "onboardingRequest" | "passwordResetRequest" | "user" | "session" | "account" | "verification"
|
||||||
txIsolationLevel: TransactionIsolationLevel
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
}
|
}
|
||||||
model: {
|
model: {
|
||||||
|
Plan: {
|
||||||
|
payload: Prisma.$PlanPayload<ExtArgs>
|
||||||
|
fields: Prisma.PlanFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.PlanFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.PlanFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.PlanFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.PlanFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.PlanFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.PlanCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.PlanCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.PlanCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.PlanDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.PlanUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.PlanDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.PlanUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.PlanUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.PlanUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.PlanAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregatePlan>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.PlanGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.PlanGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.PlanCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.PlanCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Complex: {
|
Complex: {
|
||||||
payload: Prisma.$ComplexPayload<ExtArgs>
|
payload: Prisma.$ComplexPayload<ExtArgs>
|
||||||
fields: Prisma.ComplexFieldRefs
|
fields: Prisma.ComplexFieldRefs
|
||||||
@@ -1005,77 +1083,77 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Plan: {
|
PasswordResetRequest: {
|
||||||
payload: Prisma.$PlanPayload<ExtArgs>
|
payload: Prisma.$PasswordResetRequestPayload<ExtArgs>
|
||||||
fields: Prisma.PlanFieldRefs
|
fields: Prisma.PasswordResetRequestFieldRefs
|
||||||
operations: {
|
operations: {
|
||||||
findUnique: {
|
findUnique: {
|
||||||
args: Prisma.PlanFindUniqueArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestFindUniqueArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload> | null
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload> | null
|
||||||
}
|
}
|
||||||
findUniqueOrThrow: {
|
findUniqueOrThrow: {
|
||||||
args: Prisma.PlanFindUniqueOrThrowArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestFindUniqueOrThrowArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
findFirst: {
|
findFirst: {
|
||||||
args: Prisma.PlanFindFirstArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestFindFirstArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload> | null
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload> | null
|
||||||
}
|
}
|
||||||
findFirstOrThrow: {
|
findFirstOrThrow: {
|
||||||
args: Prisma.PlanFindFirstOrThrowArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestFindFirstOrThrowArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
findMany: {
|
findMany: {
|
||||||
args: Prisma.PlanFindManyArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestFindManyArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
}
|
}
|
||||||
create: {
|
create: {
|
||||||
args: Prisma.PlanCreateArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestCreateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
createMany: {
|
createMany: {
|
||||||
args: Prisma.PlanCreateManyArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestCreateManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
createManyAndReturn: {
|
createManyAndReturn: {
|
||||||
args: Prisma.PlanCreateManyAndReturnArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestCreateManyAndReturnArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
}
|
}
|
||||||
delete: {
|
delete: {
|
||||||
args: Prisma.PlanDeleteArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestDeleteArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
update: {
|
update: {
|
||||||
args: Prisma.PlanUpdateArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestUpdateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
deleteMany: {
|
deleteMany: {
|
||||||
args: Prisma.PlanDeleteManyArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestDeleteManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
updateMany: {
|
updateMany: {
|
||||||
args: Prisma.PlanUpdateManyArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestUpdateManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
updateManyAndReturn: {
|
updateManyAndReturn: {
|
||||||
args: Prisma.PlanUpdateManyAndReturnArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestUpdateManyAndReturnArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
}
|
}
|
||||||
upsert: {
|
upsert: {
|
||||||
args: Prisma.PlanUpsertArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestUpsertArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$PlanPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
}
|
}
|
||||||
aggregate: {
|
aggregate: {
|
||||||
args: Prisma.PlanAggregateArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestAggregateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.AggregatePlan>
|
result: runtime.Types.Utils.Optional<Prisma.AggregatePasswordResetRequest>
|
||||||
}
|
}
|
||||||
groupBy: {
|
groupBy: {
|
||||||
args: Prisma.PlanGroupByArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestGroupByArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.PlanGroupByOutputType>[]
|
result: runtime.Types.Utils.Optional<Prisma.PasswordResetRequestGroupByOutputType>[]
|
||||||
}
|
}
|
||||||
count: {
|
count: {
|
||||||
args: Prisma.PlanCountArgs<ExtArgs>
|
args: Prisma.PasswordResetRequestCountArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.PlanCountAggregateOutputType> | number
|
result: runtime.Types.Utils.Optional<Prisma.PasswordResetRequestCountAggregateOutputType> | number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1153,6 +1231,228 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Session: {
|
||||||
|
payload: Prisma.$SessionPayload<ExtArgs>
|
||||||
|
fields: Prisma.SessionFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.SessionFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.SessionFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.SessionFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.SessionFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.SessionFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.SessionCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.SessionCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.SessionCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.SessionDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.SessionUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.SessionDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.SessionUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.SessionUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.SessionUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.SessionAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateSession>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.SessionGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.SessionGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.SessionCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.SessionCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Account: {
|
||||||
|
payload: Prisma.$AccountPayload<ExtArgs>
|
||||||
|
fields: Prisma.AccountFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.AccountFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.AccountFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.AccountFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.AccountFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.AccountFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.AccountCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.AccountCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.AccountCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.AccountDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.AccountUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.AccountDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.AccountUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.AccountUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.AccountUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.AccountAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateAccount>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.AccountGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AccountGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.AccountCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AccountCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Verification: {
|
||||||
|
payload: Prisma.$VerificationPayload<ExtArgs>
|
||||||
|
fields: Prisma.VerificationFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.VerificationFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.VerificationFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.VerificationFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.VerificationFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.VerificationFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.VerificationCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.VerificationCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.VerificationCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.VerificationDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.VerificationUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.VerificationDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.VerificationUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.VerificationUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.VerificationUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.VerificationAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateVerification>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.VerificationGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.VerificationGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.VerificationCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.VerificationCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} & {
|
} & {
|
||||||
other: {
|
other: {
|
||||||
@@ -1192,13 +1492,28 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
|||||||
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const PlanScalarFieldEnum = {
|
||||||
|
code: 'code',
|
||||||
|
name: 'name',
|
||||||
|
price: 'price',
|
||||||
|
rules: 'rules',
|
||||||
|
lastUpdatedAt: 'lastUpdatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ComplexScalarFieldEnum = {
|
export const ComplexScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
|
complexSlug: 'complexSlug',
|
||||||
complexName: 'complexName',
|
complexName: 'complexName',
|
||||||
physicalAddress: 'physicalAddress',
|
physicalAddress: 'physicalAddress',
|
||||||
complexSlug: 'complexSlug',
|
city: 'city',
|
||||||
|
state: 'state',
|
||||||
|
country: 'country',
|
||||||
adminEmail: 'adminEmail',
|
adminEmail: 'adminEmail',
|
||||||
planCode: 'planCode',
|
planCode: 'planCode',
|
||||||
|
isActive: 'isActive',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -1207,10 +1522,12 @@ export type ComplexScalarFieldEnum = (typeof ComplexScalarFieldEnum)[keyof typeo
|
|||||||
|
|
||||||
|
|
||||||
export const ComplexUserScalarFieldEnum = {
|
export const ComplexUserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
complexId: 'complexId',
|
complexId: 'complexId',
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
role: 'role',
|
role: 'role',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
||||||
@@ -1304,21 +1621,29 @@ export const OnboardingRequestScalarFieldEnum = {
|
|||||||
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const PlanScalarFieldEnum = {
|
export const PasswordResetRequestScalarFieldEnum = {
|
||||||
code: 'code',
|
id: 'id',
|
||||||
name: 'name',
|
email: 'email',
|
||||||
price: 'price',
|
otpHash: 'otpHash',
|
||||||
rules: 'rules',
|
otpExpiresAt: 'otpExpiresAt',
|
||||||
lastUpdatedAt: 'lastUpdatedAt'
|
otpAttempts: 'otpAttempts',
|
||||||
|
otpLastSentAt: 'otpLastSentAt',
|
||||||
|
otpResendCount: 'otpResendCount',
|
||||||
|
usedAt: 'usedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
export type PasswordResetRequestScalarFieldEnum = (typeof PasswordResetRequestScalarFieldEnum)[keyof typeof PasswordResetRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserScalarFieldEnum = {
|
export const UserScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
supabaseUserId: 'supabaseUserId',
|
supabaseUserId: 'supabaseUserId',
|
||||||
|
name: 'name',
|
||||||
email: 'email',
|
email: 'email',
|
||||||
|
emailVerified: 'emailVerified',
|
||||||
|
image: 'image',
|
||||||
fullName: 'fullName',
|
fullName: 'fullName',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
@@ -1327,6 +1652,51 @@ export const UserScalarFieldEnum = {
|
|||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SessionScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
token: 'token',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
ipAddress: 'ipAddress',
|
||||||
|
userAgent: 'userAgent',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AccountScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
accountId: 'accountId',
|
||||||
|
providerId: 'providerId',
|
||||||
|
accessToken: 'accessToken',
|
||||||
|
refreshToken: 'refreshToken',
|
||||||
|
accessTokenExpiresAt: 'accessTokenExpiresAt',
|
||||||
|
refreshTokenExpiresAt: 'refreshTokenExpiresAt',
|
||||||
|
scope: 'scope',
|
||||||
|
idToken: 'idToken',
|
||||||
|
password: 'password',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const VerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
identifier: 'identifier',
|
||||||
|
value: 'value',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type VerificationScalarFieldEnum = (typeof VerificationScalarFieldEnum)[keyof typeof VerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
@@ -1350,14 +1720,6 @@ export const QueryMode = {
|
|||||||
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
|
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
|
||||||
|
|
||||||
|
|
||||||
export const NullsOrder = {
|
|
||||||
first: 'first',
|
|
||||||
last: 'last'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
|
||||||
|
|
||||||
|
|
||||||
export const JsonNullValueFilter = {
|
export const JsonNullValueFilter = {
|
||||||
DbNull: DbNull,
|
DbNull: DbNull,
|
||||||
JsonNull: JsonNull,
|
JsonNull: JsonNull,
|
||||||
@@ -1367,6 +1729,14 @@ export const JsonNullValueFilter = {
|
|||||||
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field references
|
* Field references
|
||||||
@@ -1387,6 +1757,34 @@ export type ListStringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaMod
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Decimal'
|
||||||
|
*/
|
||||||
|
export type DecimalFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Decimal'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Decimal[]'
|
||||||
|
*/
|
||||||
|
export type ListDecimalFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Decimal[]'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Json'
|
||||||
|
*/
|
||||||
|
export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'QueryMode'
|
||||||
|
*/
|
||||||
|
export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'DateTime'
|
* Reference to a field of type 'DateTime'
|
||||||
*/
|
*/
|
||||||
@@ -1401,6 +1799,13 @@ export type ListDateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaM
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Boolean'
|
||||||
|
*/
|
||||||
|
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'ComplexUserRole'
|
* Reference to a field of type 'ComplexUserRole'
|
||||||
*/
|
*/
|
||||||
@@ -1415,13 +1820,6 @@ export type ListEnumComplexUserRoleFieldRefInput<$PrismaModel> = FieldRefInputTy
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'Boolean'
|
|
||||||
*/
|
|
||||||
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'Int'
|
* Reference to a field of type 'Int'
|
||||||
*/
|
*/
|
||||||
@@ -1436,20 +1834,6 @@ export type ListIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'Decimal'
|
|
||||||
*/
|
|
||||||
export type DecimalFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Decimal'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'Decimal[]'
|
|
||||||
*/
|
|
||||||
export type ListDecimalFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Decimal[]'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'DayOfWeek'
|
* Reference to a field of type 'DayOfWeek'
|
||||||
*/
|
*/
|
||||||
@@ -1478,20 +1862,6 @@ export type ListEnumCourtBookingStatusFieldRefInput<$PrismaModel> = FieldRefInpu
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'Json'
|
|
||||||
*/
|
|
||||||
export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'QueryMode'
|
|
||||||
*/
|
|
||||||
export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'Float'
|
* Reference to a field of type 'Float'
|
||||||
*/
|
*/
|
||||||
@@ -1600,6 +1970,7 @@ export type PrismaClientOptions = ({
|
|||||||
comments?: runtime.SqlCommenterPlugin[]
|
comments?: runtime.SqlCommenterPlugin[]
|
||||||
}
|
}
|
||||||
export type GlobalOmitConfig = {
|
export type GlobalOmitConfig = {
|
||||||
|
plan?: Prisma.PlanOmit
|
||||||
complex?: Prisma.ComplexOmit
|
complex?: Prisma.ComplexOmit
|
||||||
complexUser?: Prisma.ComplexUserOmit
|
complexUser?: Prisma.ComplexUserOmit
|
||||||
sport?: Prisma.SportOmit
|
sport?: Prisma.SportOmit
|
||||||
@@ -1608,8 +1979,11 @@ export type GlobalOmitConfig = {
|
|||||||
courtPriceRule?: Prisma.CourtPriceRuleOmit
|
courtPriceRule?: Prisma.CourtPriceRuleOmit
|
||||||
courtBooking?: Prisma.CourtBookingOmit
|
courtBooking?: Prisma.CourtBookingOmit
|
||||||
onboardingRequest?: Prisma.OnboardingRequestOmit
|
onboardingRequest?: Prisma.OnboardingRequestOmit
|
||||||
plan?: Prisma.PlanOmit
|
passwordResetRequest?: Prisma.PasswordResetRequestOmit
|
||||||
user?: Prisma.UserOmit
|
user?: Prisma.UserOmit
|
||||||
|
session?: Prisma.SessionOmit
|
||||||
|
account?: Prisma.AccountOmit
|
||||||
|
verification?: Prisma.VerificationOmit
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Types for Logging */
|
/* Types for Logging */
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const AnyNull = runtime.AnyNull
|
|||||||
|
|
||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
|
Plan: 'Plan',
|
||||||
Complex: 'Complex',
|
Complex: 'Complex',
|
||||||
ComplexUser: 'ComplexUser',
|
ComplexUser: 'ComplexUser',
|
||||||
Sport: 'Sport',
|
Sport: 'Sport',
|
||||||
@@ -59,8 +60,11 @@ export const ModelName = {
|
|||||||
CourtPriceRule: 'CourtPriceRule',
|
CourtPriceRule: 'CourtPriceRule',
|
||||||
CourtBooking: 'CourtBooking',
|
CourtBooking: 'CourtBooking',
|
||||||
OnboardingRequest: 'OnboardingRequest',
|
OnboardingRequest: 'OnboardingRequest',
|
||||||
Plan: 'Plan',
|
PasswordResetRequest: 'PasswordResetRequest',
|
||||||
User: 'User'
|
User: 'User',
|
||||||
|
Session: 'Session',
|
||||||
|
Account: 'Account',
|
||||||
|
Verification: 'Verification'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -79,13 +83,28 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
|||||||
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const PlanScalarFieldEnum = {
|
||||||
|
code: 'code',
|
||||||
|
name: 'name',
|
||||||
|
price: 'price',
|
||||||
|
rules: 'rules',
|
||||||
|
lastUpdatedAt: 'lastUpdatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ComplexScalarFieldEnum = {
|
export const ComplexScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
|
complexSlug: 'complexSlug',
|
||||||
complexName: 'complexName',
|
complexName: 'complexName',
|
||||||
physicalAddress: 'physicalAddress',
|
physicalAddress: 'physicalAddress',
|
||||||
complexSlug: 'complexSlug',
|
city: 'city',
|
||||||
|
state: 'state',
|
||||||
|
country: 'country',
|
||||||
adminEmail: 'adminEmail',
|
adminEmail: 'adminEmail',
|
||||||
planCode: 'planCode',
|
planCode: 'planCode',
|
||||||
|
isActive: 'isActive',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -94,10 +113,12 @@ export type ComplexScalarFieldEnum = (typeof ComplexScalarFieldEnum)[keyof typeo
|
|||||||
|
|
||||||
|
|
||||||
export const ComplexUserScalarFieldEnum = {
|
export const ComplexUserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
complexId: 'complexId',
|
complexId: 'complexId',
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
role: 'role',
|
role: 'role',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
||||||
@@ -191,21 +212,29 @@ export const OnboardingRequestScalarFieldEnum = {
|
|||||||
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const PlanScalarFieldEnum = {
|
export const PasswordResetRequestScalarFieldEnum = {
|
||||||
code: 'code',
|
id: 'id',
|
||||||
name: 'name',
|
email: 'email',
|
||||||
price: 'price',
|
otpHash: 'otpHash',
|
||||||
rules: 'rules',
|
otpExpiresAt: 'otpExpiresAt',
|
||||||
lastUpdatedAt: 'lastUpdatedAt'
|
otpAttempts: 'otpAttempts',
|
||||||
|
otpLastSentAt: 'otpLastSentAt',
|
||||||
|
otpResendCount: 'otpResendCount',
|
||||||
|
usedAt: 'usedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
export type PasswordResetRequestScalarFieldEnum = (typeof PasswordResetRequestScalarFieldEnum)[keyof typeof PasswordResetRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserScalarFieldEnum = {
|
export const UserScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
supabaseUserId: 'supabaseUserId',
|
supabaseUserId: 'supabaseUserId',
|
||||||
|
name: 'name',
|
||||||
email: 'email',
|
email: 'email',
|
||||||
|
emailVerified: 'emailVerified',
|
||||||
|
image: 'image',
|
||||||
fullName: 'fullName',
|
fullName: 'fullName',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
@@ -214,6 +243,51 @@ export const UserScalarFieldEnum = {
|
|||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SessionScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
token: 'token',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
ipAddress: 'ipAddress',
|
||||||
|
userAgent: 'userAgent',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AccountScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
userId: 'userId',
|
||||||
|
accountId: 'accountId',
|
||||||
|
providerId: 'providerId',
|
||||||
|
accessToken: 'accessToken',
|
||||||
|
refreshToken: 'refreshToken',
|
||||||
|
accessTokenExpiresAt: 'accessTokenExpiresAt',
|
||||||
|
refreshTokenExpiresAt: 'refreshTokenExpiresAt',
|
||||||
|
scope: 'scope',
|
||||||
|
idToken: 'idToken',
|
||||||
|
password: 'password',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const VerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
identifier: 'identifier',
|
||||||
|
value: 'value',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type VerificationScalarFieldEnum = (typeof VerificationScalarFieldEnum)[keyof typeof VerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
@@ -237,14 +311,6 @@ export const QueryMode = {
|
|||||||
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
|
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
|
||||||
|
|
||||||
|
|
||||||
export const NullsOrder = {
|
|
||||||
first: 'first',
|
|
||||||
last: 'last'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
|
||||||
|
|
||||||
|
|
||||||
export const JsonNullValueFilter = {
|
export const JsonNullValueFilter = {
|
||||||
DbNull: DbNull,
|
DbNull: DbNull,
|
||||||
JsonNull: JsonNull,
|
JsonNull: JsonNull,
|
||||||
@@ -253,3 +319,11 @@ export const JsonNullValueFilter = {
|
|||||||
|
|
||||||
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*
|
*
|
||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
export type * from './models/Plan'
|
||||||
export type * from './models/Complex'
|
export type * from './models/Complex'
|
||||||
export type * from './models/ComplexUser'
|
export type * from './models/ComplexUser'
|
||||||
export type * from './models/Sport'
|
export type * from './models/Sport'
|
||||||
@@ -16,6 +17,9 @@ export type * from './models/CourtAvailability'
|
|||||||
export type * from './models/CourtPriceRule'
|
export type * from './models/CourtPriceRule'
|
||||||
export type * from './models/CourtBooking'
|
export type * from './models/CourtBooking'
|
||||||
export type * from './models/OnboardingRequest'
|
export type * from './models/OnboardingRequest'
|
||||||
export type * from './models/Plan'
|
export type * from './models/PasswordResetRequest'
|
||||||
export type * from './models/User'
|
export type * from './models/User'
|
||||||
|
export type * from './models/Session'
|
||||||
|
export type * from './models/Account'
|
||||||
|
export type * from './models/Verification'
|
||||||
export type * from './commonInputTypes'
|
export type * from './commonInputTypes'
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -25,47 +25,59 @@ export type AggregateComplexUser = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserMinAggregateOutputType = {
|
export type ComplexUserMinAggregateOutputType = {
|
||||||
|
id: string | null
|
||||||
complexId: string | null
|
complexId: string | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
role: $Enums.ComplexUserRole | null
|
role: $Enums.ComplexUserRole | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserMaxAggregateOutputType = {
|
export type ComplexUserMaxAggregateOutputType = {
|
||||||
|
id: string | null
|
||||||
complexId: string | null
|
complexId: string | null
|
||||||
userId: string | null
|
userId: string | null
|
||||||
role: $Enums.ComplexUserRole | null
|
role: $Enums.ComplexUserRole | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCountAggregateOutputType = {
|
export type ComplexUserCountAggregateOutputType = {
|
||||||
|
id: number
|
||||||
complexId: number
|
complexId: number
|
||||||
userId: number
|
userId: number
|
||||||
role: number
|
role: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type ComplexUserMinAggregateInputType = {
|
export type ComplexUserMinAggregateInputType = {
|
||||||
|
id?: true
|
||||||
complexId?: true
|
complexId?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
role?: true
|
role?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserMaxAggregateInputType = {
|
export type ComplexUserMaxAggregateInputType = {
|
||||||
|
id?: true
|
||||||
complexId?: true
|
complexId?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
role?: true
|
role?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCountAggregateInputType = {
|
export type ComplexUserCountAggregateInputType = {
|
||||||
|
id?: true
|
||||||
complexId?: true
|
complexId?: true
|
||||||
userId?: true
|
userId?: true
|
||||||
role?: true
|
role?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
updatedAt?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,10 +154,12 @@ export type ComplexUserGroupByArgs<ExtArgs extends runtime.Types.Extensions.Inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserGroupByOutputType = {
|
export type ComplexUserGroupByOutputType = {
|
||||||
|
id: string
|
||||||
complexId: string
|
complexId: string
|
||||||
userId: string
|
userId: string
|
||||||
role: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
updatedAt: Date
|
||||||
_count: ComplexUserCountAggregateOutputType | null
|
_count: ComplexUserCountAggregateOutputType | null
|
||||||
_min: ComplexUserMinAggregateOutputType | null
|
_min: ComplexUserMinAggregateOutputType | null
|
||||||
_max: ComplexUserMaxAggregateOutputType | null
|
_max: ComplexUserMaxAggregateOutputType | null
|
||||||
@@ -170,41 +184,49 @@ export type ComplexUserWhereInput = {
|
|||||||
AND?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
AND?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
OR?: Prisma.ComplexUserWhereInput[]
|
OR?: Prisma.ComplexUserWhereInput[]
|
||||||
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
|
id?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserOrderByWithRelationInput = {
|
export type ComplexUserOrderByWithRelationInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
complexId?: Prisma.SortOrder
|
complexId?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
complex?: Prisma.ComplexOrderByWithRelationInput
|
complex?: Prisma.ComplexOrderByWithRelationInput
|
||||||
user?: Prisma.UserOrderByWithRelationInput
|
user?: Prisma.UserOrderByWithRelationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserWhereUniqueInput = Prisma.AtLeast<{
|
export type ComplexUserWhereUniqueInput = Prisma.AtLeast<{
|
||||||
|
id?: string
|
||||||
complexId_userId?: Prisma.ComplexUserComplexIdUserIdCompoundUniqueInput
|
complexId_userId?: Prisma.ComplexUserComplexIdUserIdCompoundUniqueInput
|
||||||
AND?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
AND?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
OR?: Prisma.ComplexUserWhereInput[]
|
OR?: Prisma.ComplexUserWhereInput[]
|
||||||
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||||
}, "complexId_userId">
|
}, "id" | "complexId_userId">
|
||||||
|
|
||||||
export type ComplexUserOrderByWithAggregationInput = {
|
export type ComplexUserOrderByWithAggregationInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
complexId?: Prisma.SortOrder
|
complexId?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
_count?: Prisma.ComplexUserCountOrderByAggregateInput
|
_count?: Prisma.ComplexUserCountOrderByAggregateInput
|
||||||
_max?: Prisma.ComplexUserMaxOrderByAggregateInput
|
_max?: Prisma.ComplexUserMaxOrderByAggregateInput
|
||||||
_min?: Prisma.ComplexUserMinOrderByAggregateInput
|
_min?: Prisma.ComplexUserMinOrderByAggregateInput
|
||||||
@@ -214,57 +236,73 @@ export type ComplexUserScalarWhereWithAggregatesInput = {
|
|||||||
AND?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
AND?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
||||||
OR?: Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
OR?: Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
||||||
NOT?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
||||||
|
id?: Prisma.StringWithAggregatesFilter<"ComplexUser"> | string
|
||||||
complexId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
userId?: Prisma.StringWithAggregatesFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleWithAggregatesFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleWithAggregatesFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"ComplexUser"> | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"ComplexUser"> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateInput = {
|
export type ComplexUserCreateInput = {
|
||||||
role?: $Enums.ComplexUserRole
|
id?: string
|
||||||
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
||||||
user: Prisma.UserCreateNestedOneWithoutComplexesInput
|
user: Prisma.UserCreateNestedOneWithoutComplexesInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedCreateInput = {
|
export type ComplexUserUncheckedCreateInput = {
|
||||||
|
id?: string
|
||||||
complexId: string
|
complexId: string
|
||||||
userId: string
|
userId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpdateInput = {
|
export type ComplexUserUpdateInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutComplexesNestedInput
|
user?: Prisma.UserUpdateOneRequiredWithoutComplexesNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateInput = {
|
export type ComplexUserUncheckedUpdateInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyInput = {
|
export type ComplexUserCreateManyInput = {
|
||||||
|
id?: string
|
||||||
complexId: string
|
complexId: string
|
||||||
userId: string
|
userId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpdateManyMutationInput = {
|
export type ComplexUserUpdateManyMutationInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateManyInput = {
|
export type ComplexUserUncheckedUpdateManyInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserListRelationFilter = {
|
export type ComplexUserListRelationFilter = {
|
||||||
@@ -283,24 +321,30 @@ export type ComplexUserComplexIdUserIdCompoundUniqueInput = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCountOrderByAggregateInput = {
|
export type ComplexUserCountOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
complexId?: Prisma.SortOrder
|
complexId?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserMaxOrderByAggregateInput = {
|
export type ComplexUserMaxOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
complexId?: Prisma.SortOrder
|
complexId?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserMinOrderByAggregateInput = {
|
export type ComplexUserMinOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
complexId?: Prisma.SortOrder
|
complexId?: Prisma.SortOrder
|
||||||
userId?: Prisma.SortOrder
|
userId?: Prisma.SortOrder
|
||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateNestedManyWithoutComplexInput = {
|
export type ComplexUserCreateNestedManyWithoutComplexInput = {
|
||||||
@@ -392,15 +436,19 @@ export type ComplexUserUncheckedUpdateManyWithoutUserNestedInput = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateWithoutComplexInput = {
|
export type ComplexUserCreateWithoutComplexInput = {
|
||||||
role?: $Enums.ComplexUserRole
|
id?: string
|
||||||
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
user: Prisma.UserCreateNestedOneWithoutComplexesInput
|
user: Prisma.UserCreateNestedOneWithoutComplexesInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedCreateWithoutComplexInput = {
|
export type ComplexUserUncheckedCreateWithoutComplexInput = {
|
||||||
|
id?: string
|
||||||
userId: string
|
userId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateOrConnectWithoutComplexInput = {
|
export type ComplexUserCreateOrConnectWithoutComplexInput = {
|
||||||
@@ -433,22 +481,28 @@ export type ComplexUserScalarWhereInput = {
|
|||||||
AND?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
AND?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
OR?: Prisma.ComplexUserScalarWhereInput[]
|
OR?: Prisma.ComplexUserScalarWhereInput[]
|
||||||
NOT?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
NOT?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
id?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateWithoutUserInput = {
|
export type ComplexUserCreateWithoutUserInput = {
|
||||||
role?: $Enums.ComplexUserRole
|
id?: string
|
||||||
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedCreateWithoutUserInput = {
|
export type ComplexUserUncheckedCreateWithoutUserInput = {
|
||||||
|
id?: string
|
||||||
complexId: string
|
complexId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateOrConnectWithoutUserInput = {
|
export type ComplexUserCreateOrConnectWithoutUserInput = {
|
||||||
@@ -478,90 +532,114 @@ export type ComplexUserUpdateManyWithWhereWithoutUserInput = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyComplexInput = {
|
export type ComplexUserCreateManyComplexInput = {
|
||||||
|
id?: string
|
||||||
userId: string
|
userId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpdateWithoutComplexInput = {
|
export type ComplexUserUpdateWithoutComplexInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutComplexesNestedInput
|
user?: Prisma.UserUpdateOneRequiredWithoutComplexesNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateWithoutComplexInput = {
|
export type ComplexUserUncheckedUpdateWithoutComplexInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateManyWithoutComplexInput = {
|
export type ComplexUserUncheckedUpdateManyWithoutComplexInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyUserInput = {
|
export type ComplexUserCreateManyUserInput = {
|
||||||
|
id?: string
|
||||||
complexId: string
|
complexId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpdateWithoutUserInput = {
|
export type ComplexUserUpdateWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateWithoutUserInput = {
|
export type ComplexUserUncheckedUpdateWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateManyWithoutUserInput = {
|
export type ComplexUserUncheckedUpdateManyWithoutUserInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type ComplexUserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ComplexUserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
|
id?: boolean
|
||||||
complexId?: boolean
|
complexId?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
role?: boolean
|
role?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
updatedAt?: boolean
|
||||||
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["complexUser"]>
|
}, ExtArgs["result"]["complexUser"]>
|
||||||
|
|
||||||
export type ComplexUserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ComplexUserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
|
id?: boolean
|
||||||
complexId?: boolean
|
complexId?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
role?: boolean
|
role?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
updatedAt?: boolean
|
||||||
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["complexUser"]>
|
}, ExtArgs["result"]["complexUser"]>
|
||||||
|
|
||||||
export type ComplexUserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ComplexUserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
|
id?: boolean
|
||||||
complexId?: boolean
|
complexId?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
role?: boolean
|
role?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
updatedAt?: boolean
|
||||||
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["complexUser"]>
|
}, ExtArgs["result"]["complexUser"]>
|
||||||
|
|
||||||
export type ComplexUserSelectScalar = {
|
export type ComplexUserSelectScalar = {
|
||||||
|
id?: boolean
|
||||||
complexId?: boolean
|
complexId?: boolean
|
||||||
userId?: boolean
|
userId?: boolean
|
||||||
role?: boolean
|
role?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"complexId" | "userId" | "role" | "createdAt", ExtArgs["result"]["complexUser"]>
|
export type ComplexUserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "complexId" | "userId" | "role" | "createdAt" | "updatedAt", ExtArgs["result"]["complexUser"]>
|
||||||
export type ComplexUserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ComplexUserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
complex?: boolean | Prisma.ComplexDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
@@ -582,10 +660,12 @@ export type $ComplexUserPayload<ExtArgs extends runtime.Types.Extensions.Interna
|
|||||||
user: Prisma.$UserPayload<ExtArgs>
|
user: Prisma.$UserPayload<ExtArgs>
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
|
id: string
|
||||||
complexId: string
|
complexId: string
|
||||||
userId: string
|
userId: string
|
||||||
role: $Enums.ComplexUserRole
|
role: $Enums.ComplexUserRole
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
updatedAt: Date
|
||||||
}, ExtArgs["result"]["complexUser"]>
|
}, ExtArgs["result"]["complexUser"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -669,8 +749,8 @@ export interface ComplexUserDelegate<ExtArgs extends runtime.Types.Extensions.In
|
|||||||
* // Get first 10 ComplexUsers
|
* // Get first 10 ComplexUsers
|
||||||
* const complexUsers = await prisma.complexUser.findMany({ take: 10 })
|
* const complexUsers = await prisma.complexUser.findMany({ take: 10 })
|
||||||
*
|
*
|
||||||
* // Only select the `complexId`
|
* // Only select the `id`
|
||||||
* const complexUserWithComplexIdOnly = await prisma.complexUser.findMany({ select: { complexId: true } })
|
* const complexUserWithIdOnly = await prisma.complexUser.findMany({ select: { id: true } })
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
findMany<T extends ComplexUserFindManyArgs>(args?: Prisma.SelectSubset<T, ComplexUserFindManyArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions>>
|
findMany<T extends ComplexUserFindManyArgs>(args?: Prisma.SelectSubset<T, ComplexUserFindManyArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions>>
|
||||||
@@ -714,9 +794,9 @@ export interface ComplexUserDelegate<ExtArgs extends runtime.Types.Extensions.In
|
|||||||
* ]
|
* ]
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* // Create many ComplexUsers and only return the `complexId`
|
* // Create many ComplexUsers and only return the `id`
|
||||||
* const complexUserWithComplexIdOnly = await prisma.complexUser.createManyAndReturn({
|
* const complexUserWithIdOnly = await prisma.complexUser.createManyAndReturn({
|
||||||
* select: { complexId: true },
|
* select: { id: true },
|
||||||
* data: [
|
* data: [
|
||||||
* // ... provide data here
|
* // ... provide data here
|
||||||
* ]
|
* ]
|
||||||
@@ -805,9 +885,9 @@ export interface ComplexUserDelegate<ExtArgs extends runtime.Types.Extensions.In
|
|||||||
* ]
|
* ]
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* // Update zero or more ComplexUsers and only return the `complexId`
|
* // Update zero or more ComplexUsers and only return the `id`
|
||||||
* const complexUserWithComplexIdOnly = await prisma.complexUser.updateManyAndReturn({
|
* const complexUserWithIdOnly = await prisma.complexUser.updateManyAndReturn({
|
||||||
* select: { complexId: true },
|
* select: { id: true },
|
||||||
* where: {
|
* where: {
|
||||||
* // ... provide filter here
|
* // ... provide filter here
|
||||||
* },
|
* },
|
||||||
@@ -1011,10 +1091,12 @@ export interface Prisma__ComplexUserClient<T, Null = never, ExtArgs extends runt
|
|||||||
* Fields of the ComplexUser model
|
* Fields of the ComplexUser model
|
||||||
*/
|
*/
|
||||||
export interface ComplexUserFieldRefs {
|
export interface ComplexUserFieldRefs {
|
||||||
|
readonly id: Prisma.FieldRef<"ComplexUser", 'String'>
|
||||||
readonly complexId: Prisma.FieldRef<"ComplexUser", 'String'>
|
readonly complexId: Prisma.FieldRef<"ComplexUser", 'String'>
|
||||||
readonly userId: Prisma.FieldRef<"ComplexUser", 'String'>
|
readonly userId: Prisma.FieldRef<"ComplexUser", 'String'>
|
||||||
readonly role: Prisma.FieldRef<"ComplexUser", 'ComplexUserRole'>
|
readonly role: Prisma.FieldRef<"ComplexUser", 'ComplexUserRole'>
|
||||||
readonly createdAt: Prisma.FieldRef<"ComplexUser", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"ComplexUser", 'DateTime'>
|
||||||
|
readonly updatedAt: Prisma.FieldRef<"ComplexUser", 'DateTime'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -553,14 +553,6 @@ export type IntFieldUpdateOperationsInput = {
|
|||||||
divide?: number
|
divide?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DecimalFieldUpdateOperationsInput = {
|
|
||||||
set?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
increment?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
decrement?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
multiply?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
divide?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CourtCreateNestedOneWithoutAvailabilitiesInput = {
|
export type CourtCreateNestedOneWithoutAvailabilitiesInput = {
|
||||||
create?: Prisma.XOR<Prisma.CourtCreateWithoutAvailabilitiesInput, Prisma.CourtUncheckedCreateWithoutAvailabilitiesInput>
|
create?: Prisma.XOR<Prisma.CourtCreateWithoutAvailabilitiesInput, Prisma.CourtUncheckedCreateWithoutAvailabilitiesInput>
|
||||||
connectOrCreate?: Prisma.CourtCreateOrConnectWithoutAvailabilitiesInput
|
connectOrCreate?: Prisma.CourtCreateOrConnectWithoutAvailabilitiesInput
|
||||||
|
|||||||
@@ -320,11 +320,6 @@ export type PlanUncheckedUpdateManyInput = {
|
|||||||
lastUpdatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
lastUpdatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlanNullableScalarRelationFilter = {
|
|
||||||
is?: Prisma.PlanWhereInput | null
|
|
||||||
isNot?: Prisma.PlanWhereInput | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PlanCountOrderByAggregateInput = {
|
export type PlanCountOrderByAggregateInput = {
|
||||||
code?: Prisma.SortOrder
|
code?: Prisma.SortOrder
|
||||||
name?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
@@ -355,18 +350,37 @@ export type PlanSumOrderByAggregateInput = {
|
|||||||
price?: Prisma.SortOrder
|
price?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PlanScalarRelationFilter = {
|
||||||
|
is?: Prisma.PlanWhereInput
|
||||||
|
isNot?: Prisma.PlanWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringFieldUpdateOperationsInput = {
|
||||||
|
set?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DecimalFieldUpdateOperationsInput = {
|
||||||
|
set?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
increment?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
decrement?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
multiply?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
divide?: runtime.Decimal | runtime.DecimalJsLike | number | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeFieldUpdateOperationsInput = {
|
||||||
|
set?: Date | string
|
||||||
|
}
|
||||||
|
|
||||||
export type PlanCreateNestedOneWithoutComplexesInput = {
|
export type PlanCreateNestedOneWithoutComplexesInput = {
|
||||||
create?: Prisma.XOR<Prisma.PlanCreateWithoutComplexesInput, Prisma.PlanUncheckedCreateWithoutComplexesInput>
|
create?: Prisma.XOR<Prisma.PlanCreateWithoutComplexesInput, Prisma.PlanUncheckedCreateWithoutComplexesInput>
|
||||||
connectOrCreate?: Prisma.PlanCreateOrConnectWithoutComplexesInput
|
connectOrCreate?: Prisma.PlanCreateOrConnectWithoutComplexesInput
|
||||||
connect?: Prisma.PlanWhereUniqueInput
|
connect?: Prisma.PlanWhereUniqueInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlanUpdateOneWithoutComplexesNestedInput = {
|
export type PlanUpdateOneRequiredWithoutComplexesNestedInput = {
|
||||||
create?: Prisma.XOR<Prisma.PlanCreateWithoutComplexesInput, Prisma.PlanUncheckedCreateWithoutComplexesInput>
|
create?: Prisma.XOR<Prisma.PlanCreateWithoutComplexesInput, Prisma.PlanUncheckedCreateWithoutComplexesInput>
|
||||||
connectOrCreate?: Prisma.PlanCreateOrConnectWithoutComplexesInput
|
connectOrCreate?: Prisma.PlanCreateOrConnectWithoutComplexesInput
|
||||||
upsert?: Prisma.PlanUpsertWithoutComplexesInput
|
upsert?: Prisma.PlanUpsertWithoutComplexesInput
|
||||||
disconnect?: Prisma.PlanWhereInput | boolean
|
|
||||||
delete?: Prisma.PlanWhereInput | boolean
|
|
||||||
connect?: Prisma.PlanWhereUniqueInput
|
connect?: Prisma.PlanWhereUniqueInput
|
||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.PlanUpdateToOneWithWhereWithoutComplexesInput, Prisma.PlanUpdateWithoutComplexesInput>, Prisma.PlanUncheckedUpdateWithoutComplexesInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.PlanUpdateToOneWithWhereWithoutComplexesInput, Prisma.PlanUpdateWithoutComplexesInput>, Prisma.PlanUncheckedUpdateWithoutComplexesInput>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,10 +339,6 @@ export type SportScalarRelationFilter = {
|
|||||||
isNot?: Prisma.SportWhereInput
|
isNot?: Prisma.SportWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoolFieldUpdateOperationsInput = {
|
|
||||||
set?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SportCreateNestedOneWithoutCourtsInput = {
|
export type SportCreateNestedOneWithoutCourtsInput = {
|
||||||
create?: Prisma.XOR<Prisma.SportCreateWithoutCourtsInput, Prisma.SportUncheckedCreateWithoutCourtsInput>
|
create?: Prisma.XOR<Prisma.SportCreateWithoutCourtsInput, Prisma.SportUncheckedCreateWithoutCourtsInput>
|
||||||
connectOrCreate?: Prisma.SportCreateOrConnectWithoutCourtsInput
|
connectOrCreate?: Prisma.SportCreateOrConnectWithoutCourtsInput
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ export type AggregateUser = {
|
|||||||
export type UserMinAggregateOutputType = {
|
export type UserMinAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
supabaseUserId: string | null
|
supabaseUserId: string | null
|
||||||
|
name: string | null
|
||||||
email: string | null
|
email: string | null
|
||||||
|
emailVerified: boolean | null
|
||||||
|
image: string | null
|
||||||
fullName: string | null
|
fullName: string | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
@@ -36,7 +39,10 @@ export type UserMinAggregateOutputType = {
|
|||||||
export type UserMaxAggregateOutputType = {
|
export type UserMaxAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
supabaseUserId: string | null
|
supabaseUserId: string | null
|
||||||
|
name: string | null
|
||||||
email: string | null
|
email: string | null
|
||||||
|
emailVerified: boolean | null
|
||||||
|
image: string | null
|
||||||
fullName: string | null
|
fullName: string | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
@@ -45,7 +51,10 @@ export type UserMaxAggregateOutputType = {
|
|||||||
export type UserCountAggregateOutputType = {
|
export type UserCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
supabaseUserId: number
|
supabaseUserId: number
|
||||||
|
name: number
|
||||||
email: number
|
email: number
|
||||||
|
emailVerified: number
|
||||||
|
image: number
|
||||||
fullName: number
|
fullName: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
@@ -56,7 +65,10 @@ export type UserCountAggregateOutputType = {
|
|||||||
export type UserMinAggregateInputType = {
|
export type UserMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
supabaseUserId?: true
|
||||||
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
fullName?: true
|
fullName?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -65,7 +77,10 @@ export type UserMinAggregateInputType = {
|
|||||||
export type UserMaxAggregateInputType = {
|
export type UserMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
supabaseUserId?: true
|
||||||
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
fullName?: true
|
fullName?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -74,7 +89,10 @@ export type UserMaxAggregateInputType = {
|
|||||||
export type UserCountAggregateInputType = {
|
export type UserCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
supabaseUserId?: true
|
||||||
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
fullName?: true
|
fullName?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -155,9 +173,12 @@ export type UserGroupByArgs<ExtArgs extends runtime.Types.Extensions.InternalArg
|
|||||||
|
|
||||||
export type UserGroupByOutputType = {
|
export type UserGroupByOutputType = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
supabaseUserId: string | null
|
||||||
|
name: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified: boolean
|
||||||
|
image: string | null
|
||||||
|
fullName: string | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
_count: UserCountAggregateOutputType | null
|
_count: UserCountAggregateOutputType | null
|
||||||
@@ -184,22 +205,32 @@ export type UserWhereInput = {
|
|||||||
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
OR?: Prisma.UserWhereInput[]
|
OR?: Prisma.UserWhereInput[]
|
||||||
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
id?: Prisma.UuidFilter<"User"> | string
|
id?: Prisma.StringFilter<"User"> | string
|
||||||
supabaseUserId?: Prisma.UuidFilter<"User"> | string
|
supabaseUserId?: Prisma.UuidNullableFilter<"User"> | string | null
|
||||||
|
name?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
email?: Prisma.StringFilter<"User"> | string
|
email?: Prisma.StringFilter<"User"> | string
|
||||||
fullName?: Prisma.StringFilter<"User"> | string
|
emailVerified?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
|
fullName?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
complexes?: Prisma.ComplexUserListRelationFilter
|
complexes?: Prisma.ComplexUserListRelationFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOrderByWithRelationInput = {
|
export type UserOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
supabaseUserId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
fullName?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
|
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
||||||
|
sessions?: Prisma.SessionOrderByRelationAggregateInput
|
||||||
complexes?: Prisma.ComplexUserOrderByRelationAggregateInput
|
complexes?: Prisma.ComplexUserOrderByRelationAggregateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,17 +241,25 @@ export type UserWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
OR?: Prisma.UserWhereInput[]
|
OR?: Prisma.UserWhereInput[]
|
||||||
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
fullName?: Prisma.StringFilter<"User"> | string
|
name?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
|
emailVerified?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
|
fullName?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
complexes?: Prisma.ComplexUserListRelationFilter
|
complexes?: Prisma.ComplexUserListRelationFilter
|
||||||
}, "id" | "supabaseUserId" | "email">
|
}, "id" | "supabaseUserId" | "email">
|
||||||
|
|
||||||
export type UserOrderByWithAggregationInput = {
|
export type UserOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
supabaseUserId?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
fullName?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
_count?: Prisma.UserCountOrderByAggregateInput
|
_count?: Prisma.UserCountOrderByAggregateInput
|
||||||
@@ -232,77 +271,109 @@ export type UserScalarWhereWithAggregatesInput = {
|
|||||||
AND?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
AND?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
OR?: Prisma.UserScalarWhereWithAggregatesInput[]
|
OR?: Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
NOT?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
id?: Prisma.UuidWithAggregatesFilter<"User"> | string
|
id?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
supabaseUserId?: Prisma.UuidWithAggregatesFilter<"User"> | string
|
supabaseUserId?: Prisma.UuidNullableWithAggregatesFilter<"User"> | string | null
|
||||||
|
name?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
fullName?: Prisma.StringWithAggregatesFilter<"User"> | string
|
emailVerified?: Prisma.BoolWithAggregatesFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
|
fullName?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateInput = {
|
export type UserCreateInput = {
|
||||||
id: string
|
id?: string
|
||||||
supabaseUserId: string
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateInput = {
|
export type UserUncheckedCreateInput = {
|
||||||
id: string
|
id?: string
|
||||||
supabaseUserId: string
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateInput = {
|
export type UserUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateInput = {
|
export type UserUncheckedUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateManyInput = {
|
export type UserCreateManyInput = {
|
||||||
id: string
|
id?: string
|
||||||
supabaseUserId: string
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateManyMutationInput = {
|
export type UserUpdateManyMutationInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateManyInput = {
|
export type UserUncheckedUpdateManyInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -315,7 +386,10 @@ export type UserScalarRelationFilter = {
|
|||||||
export type UserCountOrderByAggregateInput = {
|
export type UserCountOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
supabaseUserId?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
fullName?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -324,7 +398,10 @@ export type UserCountOrderByAggregateInput = {
|
|||||||
export type UserMaxOrderByAggregateInput = {
|
export type UserMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
supabaseUserId?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
fullName?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -333,7 +410,10 @@ export type UserMaxOrderByAggregateInput = {
|
|||||||
export type UserMinOrderByAggregateInput = {
|
export type UserMinOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
supabaseUserId?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
fullName?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -353,22 +433,60 @@ export type UserUpdateOneRequiredWithoutComplexesNestedInput = {
|
|||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutComplexesInput, Prisma.UserUpdateWithoutComplexesInput>, Prisma.UserUncheckedUpdateWithoutComplexesInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutComplexesInput, Prisma.UserUpdateWithoutComplexesInput>, Prisma.UserUncheckedUpdateWithoutComplexesInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutSessionsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneRequiredWithoutSessionsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutSessionsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutSessionsInput, Prisma.UserUpdateWithoutSessionsInput>, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutAccountsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneRequiredWithoutAccountsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutAccountsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutAccountsInput, Prisma.UserUpdateWithoutAccountsInput>, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutComplexesInput = {
|
export type UserCreateWithoutComplexesInput = {
|
||||||
id: string
|
id?: string
|
||||||
supabaseUserId: string
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutComplexesInput = {
|
export type UserUncheckedCreateWithoutComplexesInput = {
|
||||||
id: string
|
id?: string
|
||||||
supabaseUserId: string
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutComplexesInput = {
|
export type UserCreateOrConnectWithoutComplexesInput = {
|
||||||
@@ -389,20 +507,174 @@ export type UserUpdateToOneWithWhereWithoutComplexesInput = {
|
|||||||
|
|
||||||
export type UserUpdateWithoutComplexesInput = {
|
export type UserUpdateWithoutComplexesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutComplexesInput = {
|
export type UserUncheckedUpdateWithoutComplexesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutSessionsInput = {
|
||||||
|
id?: string
|
||||||
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutSessionsInput = {
|
||||||
|
id?: string
|
||||||
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutSessionsInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutSessionsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutSessionsInput, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutSessionsInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutSessionsInput, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutSessionsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutSessionsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutAccountsInput = {
|
||||||
|
id?: string
|
||||||
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutAccountsInput = {
|
||||||
|
id?: string
|
||||||
|
supabaseUserId?: string | null
|
||||||
|
name?: string | null
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
fullName?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutAccountsInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutAccountsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutAccountsInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutAccountsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutAccountsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
supabaseUserId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
name?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
fullName?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -411,10 +683,14 @@ export type UserUncheckedUpdateWithoutComplexesInput = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type UserCountOutputType = {
|
export type UserCountOutputType = {
|
||||||
|
accounts: number
|
||||||
|
sessions: number
|
||||||
complexes: number
|
complexes: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
||||||
|
sessions?: boolean | UserCountOutputTypeCountSessionsArgs
|
||||||
complexes?: boolean | UserCountOutputTypeCountComplexesArgs
|
complexes?: boolean | UserCountOutputTypeCountComplexesArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +704,20 @@ export type UserCountOutputTypeDefaultArgs<ExtArgs extends runtime.Types.Extensi
|
|||||||
select?: Prisma.UserCountOutputTypeSelect<ExtArgs> | null
|
select?: Prisma.UserCountOutputTypeSelect<ExtArgs> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountAccountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.AccountWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountSessionsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.SessionWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserCountOutputType without action
|
* UserCountOutputType without action
|
||||||
*/
|
*/
|
||||||
@@ -439,10 +729,15 @@ export type UserCountOutputTypeCountComplexesArgs<ExtArgs extends runtime.Types.
|
|||||||
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
supabaseUserId?: boolean
|
||||||
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
fullName?: boolean
|
fullName?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
@@ -450,7 +745,10 @@ export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = r
|
|||||||
export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
supabaseUserId?: boolean
|
||||||
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
fullName?: boolean
|
fullName?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
@@ -459,7 +757,10 @@ export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
|||||||
export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
supabaseUserId?: boolean
|
||||||
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
fullName?: boolean
|
fullName?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
@@ -468,14 +769,19 @@ export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensio
|
|||||||
export type UserSelectScalar = {
|
export type UserSelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
supabaseUserId?: boolean
|
||||||
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
fullName?: boolean
|
fullName?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "supabaseUserId" | "email" | "fullName" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "supabaseUserId" | "name" | "email" | "emailVerified" | "image" | "fullName" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
||||||
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
@@ -485,13 +791,18 @@ export type UserIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensi
|
|||||||
export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "User"
|
name: "User"
|
||||||
objects: {
|
objects: {
|
||||||
|
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
||||||
|
sessions: Prisma.$SessionPayload<ExtArgs>[]
|
||||||
complexes: Prisma.$ComplexUserPayload<ExtArgs>[]
|
complexes: Prisma.$ComplexUserPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
supabaseUserId: string | null
|
||||||
|
name: string | null
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified: boolean
|
||||||
|
image: string | null
|
||||||
|
fullName: string | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
@@ -888,6 +1199,8 @@ readonly fields: UserFieldRefs;
|
|||||||
*/
|
*/
|
||||||
export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
|
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
|
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
complexes<T extends Prisma.User$complexesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$complexesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
complexes<T extends Prisma.User$complexesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$complexesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
@@ -920,7 +1233,10 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
|
|||||||
export interface UserFieldRefs {
|
export interface UserFieldRefs {
|
||||||
readonly id: Prisma.FieldRef<"User", 'String'>
|
readonly id: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly supabaseUserId: Prisma.FieldRef<"User", 'String'>
|
readonly supabaseUserId: Prisma.FieldRef<"User", 'String'>
|
||||||
|
readonly name: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly email: Prisma.FieldRef<"User", 'String'>
|
readonly email: Prisma.FieldRef<"User", 'String'>
|
||||||
|
readonly emailVerified: Prisma.FieldRef<"User", 'Boolean'>
|
||||||
|
readonly image: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly fullName: Prisma.FieldRef<"User", 'String'>
|
readonly fullName: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
@@ -1316,6 +1632,54 @@ export type UserDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
|||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.accounts
|
||||||
|
*/
|
||||||
|
export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the Account
|
||||||
|
*/
|
||||||
|
select?: Prisma.AccountSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the Account
|
||||||
|
*/
|
||||||
|
omit?: Prisma.AccountOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.AccountInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.AccountWhereInput
|
||||||
|
orderBy?: Prisma.AccountOrderByWithRelationInput | Prisma.AccountOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.AccountWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.AccountScalarFieldEnum | Prisma.AccountScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.sessions
|
||||||
|
*/
|
||||||
|
export type User$sessionsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the Session
|
||||||
|
*/
|
||||||
|
select?: Prisma.SessionSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the Session
|
||||||
|
*/
|
||||||
|
omit?: Prisma.SessionOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.SessionInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.SessionWhereInput
|
||||||
|
orderBy?: Prisma.SessionOrderByWithRelationInput | Prisma.SessionOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.SessionWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.SessionScalarFieldEnum | Prisma.SessionScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User.complexes
|
* User.complexes
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { createApp } from '@/app'
|
import { createApp } from '@/app';
|
||||||
import { registerRoutes } from '@/register-routes'
|
import { registerAuthRoutes } from '@/auth-routes';
|
||||||
|
import { registerRoutes } from '@/register-routes';
|
||||||
|
|
||||||
const app = createApp()
|
const app = createApp();
|
||||||
registerRoutes(app)
|
registerAuthRoutes(app);
|
||||||
|
registerRoutes(app);
|
||||||
|
|
||||||
export type AppType = typeof app
|
export type AppType = typeof app;
|
||||||
export default app
|
export default app;
|
||||||
|
|||||||
56
apps/backend/src/lib/auth.ts
Normal file
56
apps/backend/src/lib/auth.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { sendMail } from '@/lib/mailer';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
import { betterAuth } from 'better-auth';
|
||||||
|
import { prismaAdapter } from 'better-auth/adapters/prisma';
|
||||||
|
import { emailOTP } from 'better-auth/plugins';
|
||||||
|
|
||||||
|
export const auth = betterAuth({
|
||||||
|
database: prismaAdapter(db, {
|
||||||
|
provider: 'postgresql',
|
||||||
|
}),
|
||||||
|
emailAndPassword: {
|
||||||
|
enabled: true,
|
||||||
|
password: {
|
||||||
|
hash: async (password) => {
|
||||||
|
return bcrypt.hash(password, 10);
|
||||||
|
},
|
||||||
|
verify: async (password, hash) => {
|
||||||
|
return bcrypt.compare(password, hash);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
emailOTP({
|
||||||
|
async sendVerificationOTP({ email, otp, type }) {
|
||||||
|
let subject = 'Tu código de verificación';
|
||||||
|
let text = `Tu código OTP es ${otp}.`;
|
||||||
|
let html = `<p>Tu código OTP es <strong>${otp}</strong>.</p>`;
|
||||||
|
|
||||||
|
if (type === 'sign-in') {
|
||||||
|
subject = 'Código de inicio de sesión';
|
||||||
|
text = `Tu código para iniciar sesión es ${otp}.`;
|
||||||
|
html = `<p>Tu código para iniciar sesión es <strong>${otp}</strong>.</p>`;
|
||||||
|
} else if (type === 'forget-password') {
|
||||||
|
subject = 'Código para restablecer contraseña';
|
||||||
|
text = `Tu código para restablecer tu contraseña es ${otp}.`;
|
||||||
|
html = `<p>Tu código para restablecer tu contraseña es <strong>${otp}</strong>.</p>`;
|
||||||
|
} else if (type === 'email-verification') {
|
||||||
|
subject = 'Verifica tu correo electrónico';
|
||||||
|
text = `Tu código de verificación es ${otp}.`;
|
||||||
|
html = `<p>Tu código de verificación es <strong>${otp}</strong>.</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendMail({
|
||||||
|
to: email,
|
||||||
|
subject,
|
||||||
|
text,
|
||||||
|
html,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Session = typeof auth.$Infer.Session.session;
|
||||||
|
export type User = typeof auth.$Infer.Session.user;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import pino from 'pino'
|
import pino from 'pino';
|
||||||
|
|
||||||
const isProd = Bun.env.NODE_ENV === 'production'
|
const isProd = Bun.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
export const logger = pino({
|
export const logger = pino({
|
||||||
level: Bun.env.LOG_LEVEL ?? (isProd ? 'info' : 'debug'),
|
level: Bun.env.LOG_LEVEL ?? (isProd ? 'info' : 'debug'),
|
||||||
@@ -15,4 +15,4 @@ export const logger = pino({
|
|||||||
ignore: 'pid,hostname',
|
ignore: 'pid,hostname',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
let cachedTransporter: ReturnType<typeof nodemailer.createTransport> | null = null
|
let cachedTransporter: ReturnType<typeof nodemailer.createTransport> | null = null;
|
||||||
|
|
||||||
function getTransporter() {
|
function getTransporter() {
|
||||||
if (cachedTransporter) return cachedTransporter
|
if (cachedTransporter) return cachedTransporter;
|
||||||
|
|
||||||
const smtpHost = Bun.env.SMTP_HOST
|
const smtpHost = Bun.env.SMTP_HOST;
|
||||||
const smtpPort = Number(Bun.env.SMTP_PORT ?? 587)
|
const smtpPort = Number(Bun.env.SMTP_PORT ?? 587);
|
||||||
const smtpUser = Bun.env.SMTP_USER
|
const smtpUser = Bun.env.SMTP_USER;
|
||||||
const smtpPass = Bun.env.SMTP_PASS
|
const smtpPass = Bun.env.SMTP_PASS;
|
||||||
|
|
||||||
if (!smtpHost || !smtpUser || !smtpPass) {
|
if (!smtpHost || !smtpUser || !smtpPass) {
|
||||||
throw new Error(
|
throw new Error('Missing SMTP env vars. Set SMTP_HOST, SMTP_PORT, SMTP_USER and SMTP_PASS.');
|
||||||
'Missing SMTP env vars. Set SMTP_HOST, SMTP_PORT, SMTP_USER and SMTP_PASS.',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedTransporter = nodemailer.createTransport({
|
cachedTransporter = nodemailer.createTransport({
|
||||||
@@ -24,21 +22,21 @@ function getTransporter() {
|
|||||||
user: smtpUser,
|
user: smtpUser,
|
||||||
pass: smtpPass,
|
pass: smtpPass,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return cachedTransporter
|
return cachedTransporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendMail(input: {
|
export async function sendMail(input: {
|
||||||
to: string
|
to: string;
|
||||||
subject: string
|
subject: string;
|
||||||
html: string
|
html: string;
|
||||||
text: string
|
text: string;
|
||||||
}) {
|
}) {
|
||||||
const smtpFrom = Bun.env.SMTP_FROM
|
const smtpFrom = Bun.env.SMTP_FROM;
|
||||||
|
|
||||||
if (!smtpFrom) {
|
if (!smtpFrom) {
|
||||||
throw new Error('Missing SMTP_FROM env var.')
|
throw new Error('Missing SMTP_FROM env var.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await getTransporter().sendMail({
|
await getTransporter().sendMail({
|
||||||
@@ -47,5 +45,5 @@ export async function sendMail(input: {
|
|||||||
subject: input.subject,
|
subject: input.subject,
|
||||||
html: input.html,
|
html: input.html,
|
||||||
text: input.text,
|
text: input.text,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaClient } from '@/generated/prisma/client';
|
||||||
import { PrismaClient } from '@/generated/prisma/client'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
|
||||||
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }
|
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
|
||||||
|
|
||||||
function hasExpectedDelegates(client: PrismaClient) {
|
function hasExpectedDelegates(client: PrismaClient) {
|
||||||
const prismaClient = client as unknown as {
|
const prismaClient = client as unknown as {
|
||||||
court?: { findMany?: unknown }
|
court?: { findMany?: unknown };
|
||||||
sport?: { findMany?: unknown }
|
sport?: { findMany?: unknown };
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
typeof prismaClient.court?.findMany === 'function' &&
|
typeof prismaClient.court?.findMany === 'function' &&
|
||||||
typeof prismaClient.sport?.findMany === 'function'
|
typeof prismaClient.sport?.findMany === 'function'
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPrismaClient() {
|
function buildPrismaClient() {
|
||||||
const databaseUrl = Bun.env.DATABASE_URL
|
const databaseUrl = Bun.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in backend environment.')
|
throw new Error('Missing DATABASE_URL in backend environment.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
return new PrismaClient({ adapter })
|
return new PrismaClient({ adapter });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPrismaClient() {
|
export function getPrismaClient() {
|
||||||
if (globalForPrisma.prisma && hasExpectedDelegates(globalForPrisma.prisma)) {
|
if (globalForPrisma.prisma && hasExpectedDelegates(globalForPrisma.prisma)) {
|
||||||
return globalForPrisma.prisma
|
return globalForPrisma.prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalForPrisma.prisma && !hasExpectedDelegates(globalForPrisma.prisma)) {
|
if (globalForPrisma.prisma && !hasExpectedDelegates(globalForPrisma.prisma)) {
|
||||||
void globalForPrisma.prisma.$disconnect()
|
void globalForPrisma.prisma.$disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const prisma = buildPrismaClient()
|
const prisma = buildPrismaClient();
|
||||||
|
|
||||||
if (Bun.env.NODE_ENV !== 'production') {
|
if (Bun.env.NODE_ENV !== 'production') {
|
||||||
globalForPrisma.prisma = prisma
|
globalForPrisma.prisma = prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prisma
|
return prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const db = getPrismaClient()
|
export const db = getPrismaClient();
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { createClient } from '@supabase/supabase-js'
|
|
||||||
|
|
||||||
let cachedClient: ReturnType<typeof createClient> | null = null
|
|
||||||
|
|
||||||
export function getSupabaseAdminClient() {
|
|
||||||
if (cachedClient) return cachedClient
|
|
||||||
|
|
||||||
const supabaseUrl = Bun.env.SUPABASE_URL ?? Bun.env.VITE_SUPABASE_URL
|
|
||||||
const supabaseServiceRoleKey = Bun.env.SUPABASE_SERVICE_ROLE_KEY
|
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseServiceRoleKey) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing Supabase admin env vars. Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in backend environment.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedClient = createClient(supabaseUrl, supabaseServiceRoleKey, {
|
|
||||||
auth: {
|
|
||||||
autoRefreshToken: false,
|
|
||||||
persistSession: false,
|
|
||||||
detectSessionInUrl: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return cachedClient
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createClient } from '@supabase/supabase-js'
|
|
||||||
|
|
||||||
const supabaseUrl = Bun.env.SUPABASE_URL ?? Bun.env.VITE_SUPABASE_URL
|
|
||||||
const supabaseAnonKey =
|
|
||||||
Bun.env.SUPABASE_ANON_KEY ?? Bun.env.VITE_SUPABASE_ANON_KEY
|
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseAnonKey) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing Supabase env vars. Set SUPABASE_URL/SUPABASE_ANON_KEY (or VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY) in backend environment.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
||||||
auth: {
|
|
||||||
autoRefreshToken: false,
|
|
||||||
persistSession: false,
|
|
||||||
detectSessionInUrl: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,50 +1,34 @@
|
|||||||
import type { MiddlewareHandler } from 'hono'
|
import { auth } from '@/lib/auth';
|
||||||
import { db } from '@/lib/prisma'
|
import { db } from '@/lib/prisma';
|
||||||
import { supabase } from '@/lib/supabase'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import type { MiddlewareHandler } from 'hono';
|
||||||
|
|
||||||
function getBearerToken(value: string | undefined): string | null {
|
export const requireAuth: MiddlewareHandler<AppEnv> = async (c, next) => {
|
||||||
if (!value) return null
|
const bearerToken = c.req.header('authorization')?.replace('Bearer ', '');
|
||||||
const [scheme, token] = value.split(' ')
|
|
||||||
if (scheme?.toLowerCase() !== 'bearer' || !token) return null
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
export const requireAuth: MiddlewareHandler<AppEnv> = async (
|
if (!bearerToken) {
|
||||||
c,
|
return c.json({ message: 'Missing bearer token.' }, 403);
|
||||||
next,
|
|
||||||
) => {
|
|
||||||
const token = getBearerToken(c.req.header('authorization'))
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
return c.json({ message: 'Missing bearer token.' }, 403)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await supabase.auth.getUser(token)
|
const { user, session } = await auth.api.getSession({
|
||||||
|
headers: { authorization: `Bearer ${bearerToken}` },
|
||||||
|
});
|
||||||
|
|
||||||
if (error || !data.user) {
|
if (!user || !session) {
|
||||||
return c.json({ message: 'Invalid or expired token.' }, 403)
|
return c.json({ message: 'Invalid or expired token.' }, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
const email = data.user.email?.toLowerCase()
|
const appUser = await db.user.findUnique({
|
||||||
|
where: { email: user.email },
|
||||||
if (!email) {
|
|
||||||
return c.json({ message: 'Auth user does not contain email.' }, 403)
|
|
||||||
}
|
|
||||||
|
|
||||||
const appUser = await db.user.findFirst({
|
|
||||||
where: {
|
|
||||||
supabaseUserId: data.user.id,
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!appUser) {
|
if (!appUser) {
|
||||||
return c.json({ message: 'User not provisioned in backend.' }, 403)
|
return c.json({ message: 'User not provisioned in backend.' }, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.set('authUser', data.user)
|
c.set('appUserId', appUser.id);
|
||||||
c.set('appUserId', appUser.id)
|
c.set('session', session);
|
||||||
await next()
|
|
||||||
}
|
await next();
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
import type { MiddlewareHandler } from 'hono'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import type { MiddlewareHandler } from 'hono';
|
||||||
|
|
||||||
function getSuperAdminEmails(): Set<string> {
|
function getSuperAdminEmails(): Set<string> {
|
||||||
const raw = Bun.env.SUPER_ADMIN_EMAILS ?? ''
|
const raw = Bun.env.SUPER_ADMIN_EMAILS ?? '';
|
||||||
const emails = raw
|
const emails = raw
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((value) => value.trim().toLowerCase())
|
.map((value) => value.trim().toLowerCase())
|
||||||
.filter(Boolean)
|
.filter(Boolean);
|
||||||
return new Set(emails)
|
return new Set(emails);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requireSuperAdmin: MiddlewareHandler<AppEnv> = async (
|
export const requireSuperAdmin: MiddlewareHandler<AppEnv> = async (c, next) => {
|
||||||
c,
|
const authUser = c.get('authUser');
|
||||||
next,
|
const role = typeof authUser.app_metadata?.role === 'string' ? authUser.app_metadata.role : null;
|
||||||
) => {
|
const email = authUser.email?.toLowerCase();
|
||||||
const authUser = c.get('authUser')
|
|
||||||
const role =
|
|
||||||
typeof authUser.app_metadata?.role === 'string'
|
|
||||||
? authUser.app_metadata.role
|
|
||||||
: null
|
|
||||||
const email = authUser.email?.toLowerCase()
|
|
||||||
|
|
||||||
if (role === 'super_admin') {
|
if (role === 'super_admin') {
|
||||||
await next()
|
await next();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email && getSuperAdminEmails().has(email)) {
|
if (email && getSuperAdminEmails().has(email)) {
|
||||||
await next()
|
await next();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json({ message: 'Este recurso requiere permisos de super admin.' }, 403)
|
return c.json({ message: 'Este recurso requiere permisos de super admin.' }, 403);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
|
import { createAdminBookingHandler } from '@/modules/admin-booking/handlers/create-admin-booking.handler';
|
||||||
|
import { listAdminBookingsHandler } from '@/modules/admin-booking/handlers/list-admin-bookings.handler';
|
||||||
|
import { updateAdminBookingStatusHandler } from '@/modules/admin-booking/handlers/update-admin-booking-status.handler';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import {
|
||||||
|
createAdminBookingSchema,
|
||||||
|
listAdminBookingsQuerySchema,
|
||||||
|
updateAdminBookingStatusSchema,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const adminBookingRoutes = new Hono<AppEnv>();
|
||||||
|
const complexIdParamsSchema = z.object({ complexId: z.uuid() });
|
||||||
|
const bookingIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
|
|
||||||
|
adminBookingRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
|
adminBookingRoutes.get(
|
||||||
|
'/complex/:complexId',
|
||||||
|
zValidator('param', complexIdParamsSchema),
|
||||||
|
zValidator('query', listAdminBookingsQuerySchema),
|
||||||
|
listAdminBookingsHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
adminBookingRoutes.post(
|
||||||
|
'/complex/:complexId',
|
||||||
|
zValidator('param', complexIdParamsSchema),
|
||||||
|
zValidator('json', createAdminBookingSchema),
|
||||||
|
createAdminBookingHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
adminBookingRoutes.patch(
|
||||||
|
'/:id/status',
|
||||||
|
zValidator('param', bookingIdParamsSchema),
|
||||||
|
zValidator('json', updateAdminBookingStatusSchema),
|
||||||
|
updateAdminBookingStatusHandler
|
||||||
|
);
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
createAdminBooking,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { CreateAdminBookingInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
|
export async function createAdminBookingHandler(c: AppContext) {
|
||||||
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
const payload = c.req.valid('json' as never) as CreateAdminBookingInput;
|
||||||
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const booking = await createAdminBooking(appUserId, complexId, payload);
|
||||||
|
return c.json(booking, 201);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
listAdminBookings,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { ListAdminBookingsQuery } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
|
export async function listAdminBookingsHandler(c: AppContext) {
|
||||||
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
const query = c.req.valid('query' as never) as ListAdminBookingsQuery;
|
||||||
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await listAdminBookings(appUserId, complexId, query);
|
||||||
|
return c.json(response);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
updateAdminBookingStatus,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { UpdateAdminBookingStatusInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type BookingIdParams = { id: string };
|
||||||
|
|
||||||
|
export async function updateAdminBookingStatusHandler(c: AppContext) {
|
||||||
|
const { id } = c.req.valid('param' as never) as BookingIdParams;
|
||||||
|
const payload = c.req.valid('json' as never) as UpdateAdminBookingStatusInput;
|
||||||
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const booking = await updateAdminBookingStatus(appUserId, id, payload);
|
||||||
|
return c.json(booking);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,527 @@
|
|||||||
|
import { randomInt } from 'node:crypto';
|
||||||
|
import { CourtBookingStatus } from '@/generated/prisma/enums';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service';
|
||||||
|
import type {
|
||||||
|
AdminBooking,
|
||||||
|
CreateAdminBookingInput,
|
||||||
|
ListAdminBookingsQuery,
|
||||||
|
UpdateAdminBookingStatusInput,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
|
type Slot = {
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DAY_OF_WEEK_BY_INDEX = [
|
||||||
|
'SUNDAY',
|
||||||
|
'MONDAY',
|
||||||
|
'TUESDAY',
|
||||||
|
'WEDNESDAY',
|
||||||
|
'THURSDAY',
|
||||||
|
'FRIDAY',
|
||||||
|
'SATURDAY',
|
||||||
|
] as const;
|
||||||
|
const BOOKING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
|
const BOOKING_CODE_LENGTH = 6;
|
||||||
|
|
||||||
|
export class AdminBookingServiceError extends Error {
|
||||||
|
status: 400 | 403 | 404 | 409;
|
||||||
|
|
||||||
|
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'AdminBookingServiceError';
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toMinutes(value: string): number {
|
||||||
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||||
|
return hours * 60 + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function minutesToTime(minutes: number): string {
|
||||||
|
const safeMinutes = Math.max(0, minutes);
|
||||||
|
const hours = Math.floor(safeMinutes / 60);
|
||||||
|
const mins = safeMinutes % 60;
|
||||||
|
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIsoDate(date: string): Date {
|
||||||
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw new AdminBookingServiceError('La fecha debe tener formato YYYY-MM-DD.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const year = Number(match[1]);
|
||||||
|
const month = Number(match[2]);
|
||||||
|
const day = Number(match[3]);
|
||||||
|
|
||||||
|
const bookingDate = new Date(Date.UTC(year, month - 1, day));
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isNaN(bookingDate.getTime()) ||
|
||||||
|
bookingDate.getUTCFullYear() !== year ||
|
||||||
|
bookingDate.getUTCMonth() + 1 !== month ||
|
||||||
|
bookingDate.getUTCDate() !== day
|
||||||
|
) {
|
||||||
|
throw new AdminBookingServiceError('La fecha enviada no es valida.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bookingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayOfWeek(date: Date) {
|
||||||
|
const dayOfWeek = DAY_OF_WEEK_BY_INDEX[date.getUTCDay()];
|
||||||
|
|
||||||
|
if (!dayOfWeek) {
|
||||||
|
throw new AdminBookingServiceError('No se pudo resolver el dia de la semana.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatIsoDate(date: Date): string {
|
||||||
|
return date.toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateBookingCode(): string {
|
||||||
|
let code = '';
|
||||||
|
|
||||||
|
for (let index = 0; index < BOOKING_CODE_LENGTH; index += 1) {
|
||||||
|
code += BOOKING_CODE_ALPHABET[randomInt(0, BOOKING_CODE_ALPHABET.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSlots(
|
||||||
|
availability: Array<{
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
}>,
|
||||||
|
slotDurationMinutes: number
|
||||||
|
): Slot[] {
|
||||||
|
const slots: Slot[] = [];
|
||||||
|
|
||||||
|
for (const range of availability) {
|
||||||
|
const start = toMinutes(range.startTime);
|
||||||
|
const end = toMinutes(range.endTime);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let current = start;
|
||||||
|
current + slotDurationMinutes <= end;
|
||||||
|
current += slotDurationMinutes
|
||||||
|
) {
|
||||||
|
slots.push({
|
||||||
|
startTime: minutesToTime(current),
|
||||||
|
endTime: minutesToTime(current + slotDurationMinutes),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureComplexAccess(complexId: string, appUserId: string) {
|
||||||
|
const complexUser = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId: appUserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
plan: {
|
||||||
|
select: {
|
||||||
|
rules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!complexUser) {
|
||||||
|
throw new AdminBookingServiceError('No tienes permisos para administrar este complejo.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return complexUser.complex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapBookingResponse(booking: {
|
||||||
|
id: string;
|
||||||
|
bookingCode: string;
|
||||||
|
bookingDate: Date;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
customerName: string;
|
||||||
|
customerPhone: string;
|
||||||
|
status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED';
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
court: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
complex: {
|
||||||
|
id: string;
|
||||||
|
complexName: string;
|
||||||
|
};
|
||||||
|
sport: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}): AdminBooking {
|
||||||
|
return {
|
||||||
|
id: booking.id,
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
complexId: booking.court.complex.id,
|
||||||
|
complexName: booking.court.complex.complexName,
|
||||||
|
courtId: booking.court.id,
|
||||||
|
courtName: booking.court.name,
|
||||||
|
sport: {
|
||||||
|
id: booking.court.sport.id,
|
||||||
|
name: booking.court.sport.name,
|
||||||
|
slug: booking.court.sport.slug,
|
||||||
|
},
|
||||||
|
date: formatIsoDate(booking.bookingDate),
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerPhone: booking.customerPhone,
|
||||||
|
status: booking.status,
|
||||||
|
createdAt: booking.createdAt.toISOString(),
|
||||||
|
updatedAt: booking.updatedAt.toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listAdminBookings(
|
||||||
|
appUserId: string,
|
||||||
|
complexId: string,
|
||||||
|
query: ListAdminBookingsQuery
|
||||||
|
) {
|
||||||
|
await ensureComplexAccess(complexId, appUserId);
|
||||||
|
const fromDate = parseIsoDate(query.fromDate);
|
||||||
|
|
||||||
|
const bookings = await db.courtBooking.findMany({
|
||||||
|
where: {
|
||||||
|
bookingDate: {
|
||||||
|
gte: fromDate,
|
||||||
|
},
|
||||||
|
court: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: [{ bookingDate: 'asc' }, { startTime: 'asc' }, { createdAt: 'asc' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = bookings.map((booking) => mapBookingResponse(booking));
|
||||||
|
|
||||||
|
return {
|
||||||
|
bookings: response,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAdminBooking(
|
||||||
|
appUserId: string,
|
||||||
|
complexId: string,
|
||||||
|
input: CreateAdminBookingInput
|
||||||
|
) {
|
||||||
|
const complex = await ensureComplexAccess(complexId, appUserId);
|
||||||
|
const bookingDate = parseIsoDate(input.date);
|
||||||
|
const dayOfWeek = getDayOfWeek(bookingDate);
|
||||||
|
|
||||||
|
const court = await db.court.findFirst({
|
||||||
|
where: {
|
||||||
|
id: input.courtId,
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
availabilities: {
|
||||||
|
where: {
|
||||||
|
dayOfWeek,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
startTime: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!court) {
|
||||||
|
throw new AdminBookingServiceError('La cancha seleccionada no existe en el complejo.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validSlots = buildSlots(court.availabilities, court.slotDurationMinutes);
|
||||||
|
const selectedStartMinutes = toMinutes(input.startTime);
|
||||||
|
const selectedEndMinutes = selectedStartMinutes + court.slotDurationMinutes;
|
||||||
|
const selectedSlot = {
|
||||||
|
startTime: input.startTime,
|
||||||
|
endTime: minutesToTime(selectedEndMinutes),
|
||||||
|
};
|
||||||
|
|
||||||
|
const slotExists = validSlots.some(
|
||||||
|
(slot) => slot.startTime === selectedSlot.startTime && slot.endTime === selectedSlot.endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!slotExists) {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'El horario seleccionado no esta disponible para esa cancha.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||||||
|
try {
|
||||||
|
const booking = await db.$transaction(async (tx) => {
|
||||||
|
if (complex.plan) {
|
||||||
|
const rules = parsePlanRules(complex.plan.rules);
|
||||||
|
const bookingsForDate = await tx.courtBooking.count({
|
||||||
|
where: {
|
||||||
|
bookingDate,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
court: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const courtsCount = await tx.court.count({
|
||||||
|
where: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const violations = evaluatePlanUsage(rules, {
|
||||||
|
courtsCount,
|
||||||
|
bookingsToday: bookingsForDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxBookingsViolation = violations.find(
|
||||||
|
(violation) => violation.code === 'MAX_BOOKINGS_PER_DAY_REACHED'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (maxBookingsViolation) {
|
||||||
|
throw new AdminBookingServiceError(maxBookingsViolation.message, 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlappingBooking = await tx.courtBooking.findFirst({
|
||||||
|
where: {
|
||||||
|
courtId: court.id,
|
||||||
|
bookingDate,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
startTime: {
|
||||||
|
lt: selectedSlot.endTime,
|
||||||
|
},
|
||||||
|
endTime: {
|
||||||
|
gt: selectedSlot.startTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (overlappingBooking) {
|
||||||
|
throw new AdminBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.courtBooking.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
bookingCode: generateBookingCode(),
|
||||||
|
courtId: court.id,
|
||||||
|
bookingDate,
|
||||||
|
startTime: selectedSlot.startTime,
|
||||||
|
endTime: selectedSlot.endTime,
|
||||||
|
customerName: input.customerName.trim(),
|
||||||
|
customerPhone: input.customerPhone.trim(),
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapBookingResponse(booking);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prismaError = error as {
|
||||||
|
code?: string;
|
||||||
|
meta?: {
|
||||||
|
target?: string[] | string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prismaError.code === 'P2002') {
|
||||||
|
const targets = Array.isArray(prismaError.meta?.target)
|
||||||
|
? prismaError.meta?.target
|
||||||
|
: [prismaError.meta?.target];
|
||||||
|
const isBookingCodeCollision = targets.some((target) =>
|
||||||
|
String(target).includes('booking_code')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isBookingCodeCollision) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AdminBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'No se pudo generar un codigo de reserva unico. Intenta nuevamente.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateAdminBookingStatus(
|
||||||
|
appUserId: string,
|
||||||
|
bookingId: string,
|
||||||
|
input: UpdateAdminBookingStatusInput
|
||||||
|
) {
|
||||||
|
const booking = await db.courtBooking.findFirst({
|
||||||
|
where: {
|
||||||
|
id: bookingId,
|
||||||
|
court: {
|
||||||
|
complex: {
|
||||||
|
users: {
|
||||||
|
some: {
|
||||||
|
userId: appUserId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!booking) {
|
||||||
|
throw new AdminBookingServiceError('Reserva no encontrada.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.status === 'CANCELLED' && booking.status !== 'CONFIRMED') {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'Solo se pueden cancelar reservas en estado confirmada.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.status === 'COMPLETED' && booking.status !== 'CONFIRMED') {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'Solo se pueden marcar como cumplidas las reservas confirmadas.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await db.courtBooking.update({
|
||||||
|
where: { id: booking.id },
|
||||||
|
data: {
|
||||||
|
status:
|
||||||
|
input.status === 'COMPLETED' ? CourtBookingStatus.COMPLETED : CourtBookingStatus.CANCELLED,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapBookingResponse(updated);
|
||||||
|
}
|
||||||
@@ -1,37 +1,33 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { createComplexSchema, updateComplexSchema } from '@repo/api-contract'
|
import { createComplexHandler } from '@/modules/complex/handlers/create-complex.handler';
|
||||||
import { Hono } from 'hono'
|
import { getComplexByIdHandler } from '@/modules/complex/handlers/get-complex-by-id.handler';
|
||||||
import { z } from 'zod'
|
import { getComplexBySlugHandler } from '@/modules/complex/handlers/get-complex-by-slug.handler';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import { listMyComplexesHandler } from '@/modules/complex/handlers/list-my-complexes.handler';
|
||||||
import { createComplexHandler } from '@/modules/complex/handlers/create-complex.handler'
|
import { updateComplexHandler } from '@/modules/complex/handlers/update-complex.handler';
|
||||||
import { getComplexByIdHandler } from '@/modules/complex/handlers/get-complex-by-id.handler'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { getComplexBySlugHandler } from '@/modules/complex/handlers/get-complex-by-slug.handler'
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import { listMyComplexesHandler } from '@/modules/complex/handlers/list-my-complexes.handler'
|
import { createComplexSchema, updateComplexSchema } from '@repo/api-contract';
|
||||||
import { updateComplexHandler } from '@/modules/complex/handlers/update-complex.handler'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const complexRoutes = new Hono<AppEnv>()
|
export const complexRoutes = new Hono<AppEnv>();
|
||||||
const complexIdParamsSchema = z.object({ id: z.uuid() })
|
const complexIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
const complexSlugParamsSchema = z.object({ slug: z.string().trim().min(1) })
|
const complexSlugParamsSchema = z.object({ slug: z.string().trim().min(1) });
|
||||||
|
|
||||||
complexRoutes.use('*', requireAuth)
|
complexRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
complexRoutes.post('/', zValidator('json', createComplexSchema), createComplexHandler)
|
complexRoutes.post('/', zValidator('json', createComplexSchema), createComplexHandler);
|
||||||
complexRoutes.get('/mine', listMyComplexesHandler)
|
complexRoutes.get('/mine', listMyComplexesHandler);
|
||||||
complexRoutes.get(
|
complexRoutes.get(
|
||||||
'/slug/:slug',
|
'/slug/:slug',
|
||||||
zValidator('param', complexSlugParamsSchema),
|
zValidator('param', complexSlugParamsSchema),
|
||||||
getComplexBySlugHandler,
|
getComplexBySlugHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
complexRoutes.get(
|
complexRoutes.get('/:id', zValidator('param', complexIdParamsSchema), getComplexByIdHandler);
|
||||||
'/:id',
|
|
||||||
zValidator('param', complexIdParamsSchema),
|
|
||||||
getComplexByIdHandler,
|
|
||||||
)
|
|
||||||
complexRoutes.patch(
|
complexRoutes.patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
zValidator('json', updateComplexSchema),
|
zValidator('json', updateComplexSchema),
|
||||||
updateComplexHandler,
|
updateComplexHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import type { CreateComplexInput } from '@repo/api-contract'
|
import { createComplex } from '@/modules/complex/services/complex.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { createComplex } from '@/modules/complex/services/complex.service'
|
import type { CreateComplexInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function createComplexHandler(c: AppContext) {
|
export async function createComplexHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as CreateComplexInput
|
const payload = c.req.valid('json' as never) as CreateComplexInput;
|
||||||
|
|
||||||
const authUser = c.get('authUser')
|
const authUser = c.get('authUser');
|
||||||
const adminEmail = authUser.email
|
const adminEmail = authUser.email;
|
||||||
|
|
||||||
if (!adminEmail) {
|
if (!adminEmail) {
|
||||||
return c.json({ message: 'El usuario autenticado no tiene email.' }, 400)
|
return c.json({ message: 'El usuario autenticado no tiene email.' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const complex = await createComplex({
|
const complex = await createComplex({
|
||||||
@@ -17,7 +17,10 @@ export async function createComplexHandler(c: AppContext) {
|
|||||||
physicalAddress: payload.physicalAddress,
|
physicalAddress: payload.physicalAddress,
|
||||||
adminEmail,
|
adminEmail,
|
||||||
planCode: payload.planCode,
|
planCode: payload.planCode,
|
||||||
})
|
city: payload.city,
|
||||||
|
state: payload.state,
|
||||||
|
country: payload.country,
|
||||||
|
});
|
||||||
|
|
||||||
return c.json(complex, 201)
|
return c.json(complex, 201);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { getComplexById } from '@/modules/complex/services/complex.service';
|
||||||
import { getComplexById } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type ComplexIdParams = { id: string }
|
type ComplexIdParams = { id: string };
|
||||||
|
|
||||||
export async function getComplexByIdHandler(c: AppContext) {
|
export async function getComplexByIdHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as ComplexIdParams
|
const { id } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
|
||||||
const complex = await getComplexById(id)
|
const complex = await getComplexById(id);
|
||||||
|
|
||||||
if (!complex) {
|
if (!complex) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { getComplexBySlug } from '@/modules/complex/services/complex.service';
|
||||||
import { getComplexBySlug } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type ComplexSlugParams = { slug: string }
|
type ComplexSlugParams = { slug: string };
|
||||||
|
|
||||||
export async function getComplexBySlugHandler(c: AppContext) {
|
export async function getComplexBySlugHandler(c: AppContext) {
|
||||||
const { slug } = c.req.valid('param' as never) as ComplexSlugParams
|
const { slug } = c.req.valid('param' as never) as ComplexSlugParams;
|
||||||
|
|
||||||
const complex = await getComplexBySlug(slug)
|
const complex = await getComplexBySlug(slug);
|
||||||
|
|
||||||
if (!complex) {
|
if (!complex) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { listMyComplexes } from '@/modules/complex/services/complex.service';
|
||||||
import { listMyComplexes } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
export async function listMyComplexesHandler(c: AppContext) {
|
export async function listMyComplexesHandler(c: AppContext) {
|
||||||
const appUserId = c.get('appUserId')
|
const appUserId = c.get('appUserId');
|
||||||
const complexes = await listMyComplexes(appUserId)
|
const complexes = await listMyComplexes(appUserId);
|
||||||
return c.json(complexes)
|
return c.json(complexes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import type { UpdateComplexInput } from '@repo/api-contract'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { getComplexById, updateComplex } from '@/modules/complex/services/complex.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { getComplexById, updateComplex } from '@/modules/complex/services/complex.service'
|
import type { UpdateComplexInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexIdParams = { id: string }
|
type ComplexIdParams = { id: string };
|
||||||
|
|
||||||
export async function updateComplexHandler(c: AppContext) {
|
export async function updateComplexHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as ComplexIdParams
|
const { id } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
const payload = c.req.valid('json' as never) as UpdateComplexInput
|
const payload = c.req.valid('json' as never) as UpdateComplexInput;
|
||||||
|
|
||||||
const existing = await getComplexById(id)
|
const existing = await getComplexById(id);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const complex = await updateComplex(id, payload)
|
const complex = await updateComplex(id, payload);
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
return c.json({ message: 'No se pudo actualizar el complejo.' }, 409)
|
return c.json({ message: 'No se pudo actualizar el complejo.' }, 409);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
import { v7 as uuidv7 } from 'uuid'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { db } from '@/lib/prisma';
|
||||||
import { db } from '@/lib/prisma'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
export type CreateComplexInput = {
|
export type CreateComplexInput = {
|
||||||
complexName: string
|
complexName: string;
|
||||||
physicalAddress: string
|
physicalAddress: string;
|
||||||
adminEmail: string
|
adminEmail: string;
|
||||||
planCode?: string
|
planCode?: string;
|
||||||
}
|
city?: string;
|
||||||
|
state?: string;
|
||||||
|
country?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateComplexInput = {
|
export type UpdateComplexInput = {
|
||||||
complexName?: string
|
complexName?: string;
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null;
|
||||||
complexSlug?: string
|
complexSlug?: string;
|
||||||
adminEmail?: string
|
adminEmail?: string;
|
||||||
planCode?: string | null
|
planCode?: string | null;
|
||||||
}
|
city?: string | null;
|
||||||
|
state?: string | null;
|
||||||
|
country?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
function slugify(value: string): string {
|
function slugify(value: string): string {
|
||||||
return value
|
return value
|
||||||
@@ -25,18 +31,15 @@ function slugify(value: string): string {
|
|||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
.replace(/-+/g, '-')
|
.replace(/-+/g, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildUniqueSlug(
|
async function buildUniqueSlug(source: string, excludeComplexId?: string): Promise<string> {
|
||||||
source: string,
|
const base = slugify(source);
|
||||||
excludeComplexId?: string,
|
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`;
|
||||||
): Promise<string> {
|
|
||||||
const base = slugify(source)
|
|
||||||
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`
|
|
||||||
|
|
||||||
let candidate = fallback
|
let candidate = fallback;
|
||||||
let index = 1
|
let index = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const existing = await db.complex.findFirst({
|
const existing = await db.complex.findFirst({
|
||||||
@@ -51,40 +54,43 @@ async function buildUniqueSlug(
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!existing) return candidate
|
if (!existing) return candidate;
|
||||||
|
|
||||||
index += 1
|
index += 1;
|
||||||
candidate = `${fallback}-${index}`
|
candidate = `${fallback}-${index}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createComplex(input: CreateComplexInput) {
|
export async function createComplex(input: CreateComplexInput) {
|
||||||
const complexSlug = await buildUniqueSlug(input.complexName)
|
const complexSlug = await buildUniqueSlug(input.complexName);
|
||||||
|
|
||||||
return db.complex.create({
|
return db.complex.create({
|
||||||
data: {
|
data: {
|
||||||
id: uuidv7(),
|
id: uuidv7(),
|
||||||
complexName: input.complexName,
|
complexName: input.complexName,
|
||||||
physicalAddress: input.physicalAddress.trim(),
|
physicalAddress: input.physicalAddress.trim(),
|
||||||
|
city: input.city?.trim() || null,
|
||||||
|
state: input.state?.trim() || null,
|
||||||
|
country: input.country?.trim() || null,
|
||||||
complexSlug,
|
complexSlug,
|
||||||
adminEmail: input.adminEmail,
|
adminEmail: input.adminEmail,
|
||||||
planCode: input.planCode,
|
planCode: input.planCode,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getComplexById(id: string) {
|
export async function getComplexById(id: string) {
|
||||||
return db.complex.findUnique({
|
return db.complex.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getComplexBySlug(slug: string) {
|
export async function getComplexBySlug(slug: string) {
|
||||||
return db.complex.findUnique({
|
return db.complex.findUnique({
|
||||||
where: { complexSlug: slug },
|
where: { complexSlug: slug },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listMyComplexes(appUserId: string) {
|
export async function listMyComplexes(appUserId: string) {
|
||||||
@@ -96,37 +102,49 @@ export async function listMyComplexes(appUserId: string) {
|
|||||||
orderBy: {
|
orderBy: {
|
||||||
createdAt: 'asc',
|
createdAt: 'asc',
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return complexUsers.map((complexUser) => complexUser.complex)
|
return complexUsers.map((complexUser) => complexUser.complex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateComplex(id: string, input: UpdateComplexInput) {
|
export async function updateComplex(id: string, input: UpdateComplexInput) {
|
||||||
const data: Record<string, unknown> = {}
|
const data: Record<string, unknown> = {};
|
||||||
|
|
||||||
if (input.complexName) {
|
if (input.complexName) {
|
||||||
data.complexName = input.complexName
|
data.complexName = input.complexName;
|
||||||
data.complexSlug = await buildUniqueSlug(input.complexName, id)
|
data.complexSlug = await buildUniqueSlug(input.complexName, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.complexSlug) {
|
if (input.complexSlug) {
|
||||||
data.complexSlug = await buildUniqueSlug(input.complexSlug, id)
|
data.complexSlug = await buildUniqueSlug(input.complexSlug, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.adminEmail) {
|
if (input.adminEmail) {
|
||||||
data.adminEmail = input.adminEmail
|
data.adminEmail = input.adminEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.planCode !== undefined) {
|
if (input.planCode !== undefined) {
|
||||||
data.planCode = input.planCode
|
data.planCode = input.planCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.physicalAddress !== undefined) {
|
if (input.physicalAddress !== undefined) {
|
||||||
data.physicalAddress = input.physicalAddress?.trim() ?? null
|
data.physicalAddress = input.physicalAddress?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.city !== undefined) {
|
||||||
|
data.city = input.city?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.state !== undefined) {
|
||||||
|
data.state = input.state?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.country !== undefined) {
|
||||||
|
data.country = input.country?.trim() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.complex.update({
|
return db.complex.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: data as Prisma.ComplexUncheckedUpdateInput,
|
data: data as Prisma.ComplexUncheckedUpdateInput,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { createCourtSchema, updateCourtSchema } from '@repo/api-contract'
|
import { createCourtHandler } from '@/modules/court/handlers/create-court.handler';
|
||||||
import { Hono } from 'hono'
|
import { listCourtsByComplexHandler } from '@/modules/court/handlers/list-courts-by-complex.handler';
|
||||||
import { z } from 'zod'
|
import { updateCourtHandler } from '@/modules/court/handlers/update-court.handler';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { createCourtHandler } from '@/modules/court/handlers/create-court.handler'
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import { listCourtsByComplexHandler } from '@/modules/court/handlers/list-courts-by-complex.handler'
|
import { createCourtSchema, updateCourtSchema } from '@repo/api-contract';
|
||||||
import { updateCourtHandler } from '@/modules/court/handlers/update-court.handler'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const courtRoutes = new Hono<AppEnv>()
|
export const courtRoutes = new Hono<AppEnv>();
|
||||||
const complexIdParamsSchema = z.object({ complexId: z.uuid() })
|
const complexIdParamsSchema = z.object({ complexId: z.uuid() });
|
||||||
const courtIdParamsSchema = z.object({ id: z.uuid() })
|
const courtIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
|
|
||||||
courtRoutes.use('*', requireAuth)
|
courtRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
courtRoutes.get(
|
courtRoutes.get(
|
||||||
'/complex/:complexId',
|
'/complex/:complexId',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
listCourtsByComplexHandler,
|
listCourtsByComplexHandler
|
||||||
)
|
);
|
||||||
courtRoutes.post(
|
courtRoutes.post(
|
||||||
'/complex/:complexId',
|
'/complex/:complexId',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
zValidator('json', createCourtSchema),
|
zValidator('json', createCourtSchema),
|
||||||
createCourtHandler,
|
createCourtHandler
|
||||||
)
|
);
|
||||||
courtRoutes.patch(
|
courtRoutes.patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
zValidator('param', courtIdParamsSchema),
|
zValidator('param', courtIdParamsSchema),
|
||||||
zValidator('json', updateCourtSchema),
|
zValidator('json', updateCourtSchema),
|
||||||
updateCourtHandler,
|
updateCourtHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import type { CreateCourtInput } from '@repo/api-contract'
|
import { CourtServiceError, createCourt } from '@/modules/court/services/court.service';
|
||||||
import { CourtServiceError, createCourt } from '@/modules/court/services/court.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { CreateCourtInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexIdParams = { complexId: string }
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
export async function createCourtHandler(c: AppContext) {
|
export async function createCourtHandler(c: AppContext) {
|
||||||
const { complexId } = c.req.valid('param' as never) as ComplexIdParams
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
const payload = c.req.valid('json' as never) as CreateCourtInput
|
const payload = c.req.valid('json' as never) as CreateCourtInput;
|
||||||
const appUserId = c.get('appUserId')
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const court = await createCourt(appUserId, complexId, payload)
|
const court = await createCourt(appUserId, complexId, payload);
|
||||||
return c.json(court, 201)
|
return c.json(court, 201);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof CourtServiceError) {
|
if (error instanceof CourtServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { CourtServiceError, listCourtsByComplex } from '@/modules/court/services/court.service'
|
import { CourtServiceError, listCourtsByComplex } from '@/modules/court/services/court.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type ComplexIdParams = { complexId: string }
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
export async function listCourtsByComplexHandler(c: AppContext) {
|
export async function listCourtsByComplexHandler(c: AppContext) {
|
||||||
const { complexId } = c.req.valid('param' as never) as ComplexIdParams
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
const appUserId = c.get('appUserId')
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const courts = await listCourtsByComplex(complexId, appUserId)
|
const courts = await listCourtsByComplex(complexId, appUserId);
|
||||||
return c.json(courts)
|
return c.json(courts);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof CourtServiceError) {
|
if (error instanceof CourtServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import type { UpdateCourtInput } from '@repo/api-contract'
|
import { CourtServiceError, updateCourt } from '@/modules/court/services/court.service';
|
||||||
import { CourtServiceError, updateCourt } from '@/modules/court/services/court.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { UpdateCourtInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type CourtIdParams = { id: string }
|
type CourtIdParams = { id: string };
|
||||||
|
|
||||||
export async function updateCourtHandler(c: AppContext) {
|
export async function updateCourtHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as CourtIdParams
|
const { id } = c.req.valid('param' as never) as CourtIdParams;
|
||||||
const payload = c.req.valid('json' as never) as UpdateCourtInput
|
const payload = c.req.valid('json' as never) as UpdateCourtInput;
|
||||||
const appUserId = c.get('appUserId')
|
const appUserId = c.get('appUserId');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const court = await updateCourt(appUserId, id, payload)
|
const court = await updateCourt(appUserId, id, payload);
|
||||||
return c.json(court)
|
return c.json(court);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof CourtServiceError) {
|
if (error instanceof CourtServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +1,61 @@
|
|||||||
import type { CreateCourtInput, DayOfWeek, UpdateCourtInput } from '@repo/api-contract'
|
import type { Court, CourtAvailability, CourtPriceRule, Sport } from '@/generated/prisma/client';
|
||||||
import { v7 as uuidv7 } from 'uuid'
|
import { db } from '@/lib/prisma';
|
||||||
import type {
|
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service';
|
||||||
Court,
|
import type { CreateCourtInput, DayOfWeek, UpdateCourtInput } from '@repo/api-contract';
|
||||||
CourtAvailability,
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
CourtPriceRule,
|
|
||||||
Sport,
|
|
||||||
} from '@/generated/prisma/client'
|
|
||||||
import { db } from '@/lib/prisma'
|
|
||||||
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service'
|
|
||||||
|
|
||||||
type CourtWithRelations = Court & {
|
type CourtWithRelations = Court & {
|
||||||
sport: Sport
|
sport: Sport;
|
||||||
availabilities: CourtAvailability[]
|
availabilities: CourtAvailability[];
|
||||||
priceRules: CourtPriceRule[]
|
priceRules: CourtPriceRule[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export class CourtServiceError extends Error {
|
export class CourtServiceError extends Error {
|
||||||
status: 400 | 403 | 404 | 409
|
status: 400 | 403 | 404 | 409;
|
||||||
|
|
||||||
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
||||||
super(message)
|
super(message);
|
||||||
this.name = 'CourtServiceError'
|
this.name = 'CourtServiceError';
|
||||||
this.status = status
|
this.status = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMinutes(value: string): number {
|
function toMinutes(value: string): number {
|
||||||
const [hours, minutes] = value.split(':').map((part) => Number(part))
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||||
return hours * 60 + minutes
|
return hours * 60 + minutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertAvailabilityRanges(
|
function assertAvailabilityRanges(
|
||||||
availability: Array<{
|
availability: Array<{
|
||||||
dayOfWeek: DayOfWeek
|
dayOfWeek: DayOfWeek;
|
||||||
startTime: string
|
startTime: string;
|
||||||
endTime: string
|
endTime: string;
|
||||||
}>,
|
}>
|
||||||
) {
|
) {
|
||||||
const grouped = new Map<DayOfWeek, Array<{ start: number; end: number }>>()
|
const grouped = new Map<DayOfWeek, Array<{ start: number; end: number }>>();
|
||||||
|
|
||||||
for (const range of availability) {
|
for (const range of availability) {
|
||||||
const start = toMinutes(range.startTime)
|
const start = toMinutes(range.startTime);
|
||||||
const end = toMinutes(range.endTime)
|
const end = toMinutes(range.endTime);
|
||||||
|
|
||||||
if (start >= end) {
|
if (start >= end) {
|
||||||
throw new CourtServiceError(
|
throw new CourtServiceError(`El rango ${range.startTime}-${range.endTime} es invalido.`, 400);
|
||||||
`El rango ${range.startTime}-${range.endTime} es invalido.`,
|
|
||||||
400,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const current = grouped.get(range.dayOfWeek) ?? []
|
const current = grouped.get(range.dayOfWeek) ?? [];
|
||||||
current.push({ start, end })
|
current.push({ start, end });
|
||||||
grouped.set(range.dayOfWeek, current)
|
grouped.set(range.dayOfWeek, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ranges of grouped.values()) {
|
for (const ranges of grouped.values()) {
|
||||||
ranges.sort((a, b) => a.start - b.start)
|
ranges.sort((a, b) => a.start - b.start);
|
||||||
|
|
||||||
for (let index = 1; index < ranges.length; index += 1) {
|
for (let index = 1; index < ranges.length; index += 1) {
|
||||||
const previous = ranges[index - 1]
|
const previous = ranges[index - 1];
|
||||||
const current = ranges[index]
|
const current = ranges[index];
|
||||||
|
|
||||||
if (previous.end > current.start) {
|
if (previous.end > current.start) {
|
||||||
throw new CourtServiceError(
|
throw new CourtServiceError('Hay rangos horarios superpuestos para el mismo dia.', 400);
|
||||||
'Hay rangos horarios superpuestos para el mismo dia.',
|
|
||||||
400,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,16 +81,13 @@ async function ensureComplexAccess(complexId: string, appUserId: string) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!complexUser) {
|
if (!complexUser) {
|
||||||
throw new CourtServiceError(
|
throw new CourtServiceError('No tienes permisos para administrar este complejo.', 403);
|
||||||
'No tienes permisos para administrar este complejo.',
|
|
||||||
403,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return complexUser.complex
|
return complexUser.complex;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureActiveSport(sportId: string) {
|
async function ensureActiveSport(sportId: string) {
|
||||||
@@ -111,10 +97,10 @@ async function ensureActiveSport(sportId: string) {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!sport) {
|
if (!sport) {
|
||||||
throw new CourtServiceError('El deporte seleccionado no existe o esta inactivo.', 400)
|
throw new CourtServiceError('El deporte seleccionado no existe o esta inactivo.', 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,28 +114,26 @@ async function enforcePlanCourtLimit(complexId: string) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!complex?.plan) {
|
if (!complex?.plan) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rules = parsePlanRules(complex.plan.rules)
|
const rules = parsePlanRules(complex.plan.rules);
|
||||||
const courtsCount = await db.court.count({
|
const courtsCount = await db.court.count({
|
||||||
where: { complexId },
|
where: { complexId },
|
||||||
})
|
});
|
||||||
|
|
||||||
const violations = evaluatePlanUsage(rules, {
|
const violations = evaluatePlanUsage(rules, {
|
||||||
courtsCount,
|
courtsCount,
|
||||||
bookingsToday: 0,
|
bookingsToday: 0,
|
||||||
})
|
});
|
||||||
|
|
||||||
const maxCourtViolation = violations.find(
|
const maxCourtViolation = violations.find((violation) => violation.code === 'MAX_COURTS_REACHED');
|
||||||
(violation) => violation.code === 'MAX_COURTS_REACHED',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (maxCourtViolation) {
|
if (maxCourtViolation) {
|
||||||
throw new CourtServiceError(maxCourtViolation.message, 409)
|
throw new CourtServiceError(maxCourtViolation.message, 409);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +159,7 @@ async function getCourtByIdForUser(courtId: string, appUserId: string) {
|
|||||||
orderBy: [{ dayOfWeek: 'asc' }, { startTime: 'asc' }],
|
orderBy: [{ dayOfWeek: 'asc' }, { startTime: 'asc' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapCourtResponse(court: CourtWithRelations) {
|
function mapCourtResponse(court: CourtWithRelations) {
|
||||||
@@ -208,11 +192,11 @@ function mapCourtResponse(court: CourtWithRelations) {
|
|||||||
hasCustomPricing: court.priceRules.length > 0,
|
hasCustomPricing: court.priceRules.length > 0,
|
||||||
createdAt: court.createdAt.toISOString(),
|
createdAt: court.createdAt.toISOString(),
|
||||||
updatedAt: court.updatedAt.toISOString(),
|
updatedAt: court.updatedAt.toISOString(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listCourtsByComplex(complexId: string, appUserId: string) {
|
export async function listCourtsByComplex(complexId: string, appUserId: string) {
|
||||||
await ensureComplexAccess(complexId, appUserId)
|
await ensureComplexAccess(complexId, appUserId);
|
||||||
|
|
||||||
const courts = await db.court.findMany({
|
const courts = await db.court.findMany({
|
||||||
where: { complexId },
|
where: { complexId },
|
||||||
@@ -229,20 +213,16 @@ export async function listCourtsByComplex(complexId: string, appUserId: string)
|
|||||||
orderBy: {
|
orderBy: {
|
||||||
createdAt: 'asc',
|
createdAt: 'asc',
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return courts.map((court) => mapCourtResponse(court))
|
return courts.map((court) => mapCourtResponse(court));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createCourt(
|
export async function createCourt(appUserId: string, complexId: string, input: CreateCourtInput) {
|
||||||
appUserId: string,
|
await ensureComplexAccess(complexId, appUserId);
|
||||||
complexId: string,
|
await ensureActiveSport(input.sportId);
|
||||||
input: CreateCourtInput,
|
await enforcePlanCourtLimit(complexId);
|
||||||
) {
|
assertAvailabilityRanges(input.availability);
|
||||||
await ensureComplexAccess(complexId, appUserId)
|
|
||||||
await ensureActiveSport(input.sportId)
|
|
||||||
await enforcePlanCourtLimit(complexId)
|
|
||||||
assertAvailabilityRanges(input.availability)
|
|
||||||
|
|
||||||
const createdCourt = await db.$transaction(async (tx) => {
|
const createdCourt = await db.$transaction(async (tx) => {
|
||||||
const court = await tx.court.create({
|
const court = await tx.court.create({
|
||||||
@@ -254,7 +234,7 @@ export async function createCourt(
|
|||||||
slotDurationMinutes: input.slotDurationMinutes,
|
slotDurationMinutes: input.slotDurationMinutes,
|
||||||
basePrice: input.basePrice,
|
basePrice: input.basePrice,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
await tx.courtAvailability.createMany({
|
await tx.courtAvailability.createMany({
|
||||||
data: input.availability.map((availability) => ({
|
data: input.availability.map((availability) => ({
|
||||||
@@ -264,37 +244,33 @@ export async function createCourt(
|
|||||||
startTime: availability.startTime,
|
startTime: availability.startTime,
|
||||||
endTime: availability.endTime,
|
endTime: availability.endTime,
|
||||||
})),
|
})),
|
||||||
})
|
});
|
||||||
|
|
||||||
return court
|
return court;
|
||||||
})
|
});
|
||||||
|
|
||||||
const court = await getCourtByIdForUser(createdCourt.id, appUserId)
|
const court = await getCourtByIdForUser(createdCourt.id, appUserId);
|
||||||
|
|
||||||
if (!court) {
|
if (!court) {
|
||||||
throw new CourtServiceError('No se pudo obtener la cancha creada.', 404)
|
throw new CourtServiceError('No se pudo obtener la cancha creada.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapCourtResponse(court)
|
return mapCourtResponse(court);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateCourt(
|
export async function updateCourt(appUserId: string, courtId: string, input: UpdateCourtInput) {
|
||||||
appUserId: string,
|
const existingCourt = await getCourtByIdForUser(courtId, appUserId);
|
||||||
courtId: string,
|
|
||||||
input: UpdateCourtInput,
|
|
||||||
) {
|
|
||||||
const existingCourt = await getCourtByIdForUser(courtId, appUserId)
|
|
||||||
|
|
||||||
if (!existingCourt) {
|
if (!existingCourt) {
|
||||||
throw new CourtServiceError('Cancha no encontrada.', 404)
|
throw new CourtServiceError('Cancha no encontrada.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.sportId) {
|
if (input.sportId) {
|
||||||
await ensureActiveSport(input.sportId)
|
await ensureActiveSport(input.sportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.availability) {
|
if (input.availability) {
|
||||||
assertAvailabilityRanges(input.availability)
|
assertAvailabilityRanges(input.availability);
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.$transaction(async (tx) => {
|
await db.$transaction(async (tx) => {
|
||||||
@@ -310,12 +286,12 @@ export async function updateCourt(
|
|||||||
: {}),
|
: {}),
|
||||||
...(input.basePrice !== undefined ? { basePrice: input.basePrice } : {}),
|
...(input.basePrice !== undefined ? { basePrice: input.basePrice } : {}),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (input.availability) {
|
if (input.availability) {
|
||||||
await tx.courtAvailability.deleteMany({
|
await tx.courtAvailability.deleteMany({
|
||||||
where: { courtId },
|
where: { courtId },
|
||||||
})
|
});
|
||||||
|
|
||||||
await tx.courtAvailability.createMany({
|
await tx.courtAvailability.createMany({
|
||||||
data: input.availability.map((availability) => ({
|
data: input.availability.map((availability) => ({
|
||||||
@@ -325,15 +301,15 @@ export async function updateCourt(
|
|||||||
startTime: availability.startTime,
|
startTime: availability.startTime,
|
||||||
endTime: availability.endTime,
|
endTime: availability.endTime,
|
||||||
})),
|
})),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const updatedCourt = await getCourtByIdForUser(courtId, appUserId)
|
const updatedCourt = await getCourtByIdForUser(courtId, appUserId);
|
||||||
|
|
||||||
if (!updatedCourt) {
|
if (!updatedCourt) {
|
||||||
throw new CourtServiceError('Cancha no encontrada.', 404)
|
throw new CourtServiceError('Cancha no encontrada.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapCourtResponse(updatedCourt)
|
return mapCourtResponse(updatedCourt);
|
||||||
}
|
}
|
||||||
|
|||||||
14
apps/backend/src/modules/health-check/health-check.routes.ts
Normal file
14
apps/backend/src/modules/health-check/health-check.routes.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { AppEnv } from '@/types/hono';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
|
export const healthCheckRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
|
healthCheckRoutes.get('/', (c) => {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
status: 'healthy',
|
||||||
|
timeStamp: new Date(),
|
||||||
|
},
|
||||||
|
200
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
import type { OnboardingCompleteInput } from '@repo/api-contract'
|
|
||||||
import type { AppContext } from '@/types/hono'
|
|
||||||
import {
|
import {
|
||||||
OnboardingError,
|
OnboardingError,
|
||||||
completeOnboarding,
|
completeOnboarding,
|
||||||
} from '@/modules/onboarding/services/onboarding.service'
|
} from '@/modules/onboarding/services/onboarding.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { OnboardingCompleteInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function completeOnboardingHandler(c: AppContext) {
|
export async function completeOnboardingHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as OnboardingCompleteInput
|
const payload = c.req.valid('json' as never) as OnboardingCompleteInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await completeOnboarding(payload)
|
const result = await completeOnboarding(payload);
|
||||||
return c.json({
|
return c.json({
|
||||||
message: 'Onboarding completado correctamente.',
|
message: 'Onboarding completado correctamente.',
|
||||||
...result,
|
...result,
|
||||||
})
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof OnboardingError) {
|
if (error instanceof OnboardingError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
return c.json({ message: error.message }, 400)
|
return c.json({ message: error.message }, 400);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import type { OnboardingResendOtpInput } from '@repo/api-contract'
|
|
||||||
import type { AppContext } from '@/types/hono'
|
|
||||||
import {
|
import {
|
||||||
OnboardingError,
|
OnboardingError,
|
||||||
resendOnboardingOtp,
|
resendOnboardingOtp,
|
||||||
} from '@/modules/onboarding/services/onboarding.service'
|
} from '@/modules/onboarding/services/onboarding.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { OnboardingResendOtpInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function resendOtpHandler(c: AppContext) {
|
export async function resendOtpHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as OnboardingResendOtpInput
|
const payload = c.req.valid('json' as never) as OnboardingResendOtpInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await resendOnboardingOtp(payload)
|
const result = await resendOnboardingOtp(payload);
|
||||||
return c.json(result)
|
return c.json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof OnboardingError) {
|
if (error instanceof OnboardingError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
return c.json({ message: error.message }, 400)
|
return c.json({ message: error.message }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { OnboardingStartInput } from '@repo/api-contract'
|
import { startOnboarding } from '@/modules/onboarding/services/onboarding.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { startOnboarding } from '@/modules/onboarding/services/onboarding.service'
|
import type { OnboardingStartInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function startOnboardingHandler(c: AppContext) {
|
export async function startOnboardingHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as OnboardingStartInput
|
const payload = c.req.valid('json' as never) as OnboardingStartInput;
|
||||||
|
|
||||||
const result = await startOnboarding(payload)
|
const result = await startOnboarding(payload);
|
||||||
return c.json(result, 202)
|
return c.json(result, 202);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { OnboardingVerifyOtpInput } from '@repo/api-contract'
|
import { verifyOnboardingOtp } from '@/modules/onboarding/services/onboarding.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { verifyOnboardingOtp } from '@/modules/onboarding/services/onboarding.service'
|
import type { OnboardingVerifyOtpInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function verifyOtpHandler(c: AppContext) {
|
export async function verifyOtpHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as OnboardingVerifyOtpInput
|
const payload = c.req.valid('json' as never) as OnboardingVerifyOtpInput;
|
||||||
const result = await verifyOnboardingOtp(payload)
|
const result = await verifyOnboardingOtp(payload);
|
||||||
|
|
||||||
return c.json(result)
|
return c.json(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,35 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { completeOnboardingHandler } from '@/modules/onboarding/handlers/complete-onboarding.handler';
|
||||||
|
import { resendOtpHandler } from '@/modules/onboarding/handlers/resend-otp.handler';
|
||||||
|
import { startOnboardingHandler } from '@/modules/onboarding/handlers/start-onboarding.handler';
|
||||||
|
import { verifyOtpHandler } from '@/modules/onboarding/handlers/verify-otp.handler';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import {
|
import {
|
||||||
onboardingCompleteSchema,
|
onboardingCompleteSchema,
|
||||||
onboardingResendOtpSchema,
|
onboardingResendOtpSchema,
|
||||||
onboardingStartSchema,
|
onboardingStartSchema,
|
||||||
onboardingVerifyOtpSchema,
|
onboardingVerifyOtpSchema,
|
||||||
} from '@repo/api-contract'
|
} from '@repo/api-contract';
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono';
|
||||||
import { completeOnboardingHandler } from '@/modules/onboarding/handlers/complete-onboarding.handler'
|
|
||||||
import { resendOtpHandler } from '@/modules/onboarding/handlers/resend-otp.handler'
|
|
||||||
import { startOnboardingHandler } from '@/modules/onboarding/handlers/start-onboarding.handler'
|
|
||||||
import { verifyOtpHandler } from '@/modules/onboarding/handlers/verify-otp.handler'
|
|
||||||
import type { AppEnv } from '@/types/hono'
|
|
||||||
|
|
||||||
export const onboardingRoutes = new Hono<AppEnv>()
|
export const onboardingRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
onboardingRoutes.post(
|
onboardingRoutes.post('/start', zValidator('json', onboardingStartSchema), startOnboardingHandler);
|
||||||
'/start',
|
|
||||||
zValidator('json', onboardingStartSchema),
|
|
||||||
startOnboardingHandler,
|
|
||||||
)
|
|
||||||
|
|
||||||
onboardingRoutes.post(
|
onboardingRoutes.post(
|
||||||
'/verify-otp',
|
'/verify-otp',
|
||||||
zValidator('json', onboardingVerifyOtpSchema),
|
zValidator('json', onboardingVerifyOtpSchema),
|
||||||
verifyOtpHandler,
|
verifyOtpHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
onboardingRoutes.post(
|
onboardingRoutes.post(
|
||||||
'/resend-otp',
|
'/resend-otp',
|
||||||
zValidator('json', onboardingResendOtpSchema),
|
zValidator('json', onboardingResendOtpSchema),
|
||||||
resendOtpHandler,
|
resendOtpHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
onboardingRoutes.post(
|
onboardingRoutes.post(
|
||||||
'/complete',
|
'/complete',
|
||||||
zValidator('json', onboardingCompleteSchema),
|
zValidator('json', onboardingCompleteSchema),
|
||||||
completeOnboardingHandler,
|
completeOnboardingHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,86 +1,85 @@
|
|||||||
import { createHash, randomInt } from 'node:crypto'
|
import { createHash, randomInt } from 'node:crypto';
|
||||||
import { v7 as uuidv7 } from 'uuid'
|
import { auth } from '@/lib/auth';
|
||||||
import type { User as SupabaseAuthUser } from '@supabase/supabase-js'
|
import { sendMail } from '@/lib/mailer';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
import type {
|
import type {
|
||||||
OnboardingCompleteInput,
|
OnboardingCompleteInput,
|
||||||
OnboardingResendOtpInput,
|
OnboardingResendOtpInput,
|
||||||
OnboardingStartInput,
|
OnboardingStartInput,
|
||||||
OnboardingVerifyOtpInput,
|
OnboardingVerifyOtpInput,
|
||||||
} from '@repo/api-contract'
|
} from '@repo/api-contract';
|
||||||
import { db } from '@/lib/prisma'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
import { sendMail } from '@/lib/mailer'
|
|
||||||
import { getSupabaseAdminClient } from '@/lib/supabase-admin'
|
|
||||||
|
|
||||||
const OTP_LENGTH = 6
|
const OTP_LENGTH = 6;
|
||||||
const OTP_TTL_MINUTES = Number(Bun.env.ONBOARDING_OTP_TTL_MINUTES ?? 10)
|
const OTP_TTL_MINUTES = Number(Bun.env.ONBOARDING_OTP_TTL_MINUTES ?? 10);
|
||||||
const OTP_MAX_ATTEMPTS = Number(Bun.env.ONBOARDING_OTP_MAX_ATTEMPTS ?? 5)
|
const OTP_MAX_ATTEMPTS = Number(Bun.env.ONBOARDING_OTP_MAX_ATTEMPTS ?? 5);
|
||||||
const OTP_RESEND_COOLDOWN_SECONDS = Number(
|
const OTP_RESEND_COOLDOWN_SECONDS = Number(Bun.env.ONBOARDING_OTP_RESEND_COOLDOWN_SECONDS ?? 30);
|
||||||
Bun.env.ONBOARDING_OTP_RESEND_COOLDOWN_SECONDS ?? 30,
|
|
||||||
)
|
|
||||||
|
|
||||||
type VerifyOtpResult = {
|
type VerifyOtpResult = {
|
||||||
message: string
|
message: string;
|
||||||
verified: boolean
|
verified: boolean;
|
||||||
requestId: string
|
requestId: string;
|
||||||
email: string | null
|
email: string | null;
|
||||||
expiresAt: string | null
|
expiresAt: string | null;
|
||||||
remainingAttempts: number
|
remainingAttempts: number;
|
||||||
cooldownSeconds: number
|
cooldownSeconds: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
type ResendOtpResult = {
|
type ResendOtpResult = {
|
||||||
message: string
|
message: string;
|
||||||
requestId: string
|
requestId: string;
|
||||||
email: string
|
email: string;
|
||||||
expiresAt: string
|
expiresAt: string;
|
||||||
cooldownSeconds: number
|
cooldownSeconds: number;
|
||||||
remainingAttempts: number
|
remainingAttempts: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
type StartOnboardingResult = {
|
type StartOnboardingResult = {
|
||||||
message: string
|
message: string;
|
||||||
requestId: string
|
requestId: string;
|
||||||
email: string
|
email: string;
|
||||||
expiresAt: string
|
expiresAt: string;
|
||||||
cooldownSeconds: number
|
cooldownSeconds: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class OnboardingError extends Error {
|
export class OnboardingError extends Error {
|
||||||
status: 400 | 404 | 409 | 429
|
status: 400 | 404 | 409 | 429;
|
||||||
|
|
||||||
constructor(message: string, status: 400 | 404 | 409 | 429 = 400) {
|
constructor(message: string, status: 400 | 404 | 409 | 429 = 400) {
|
||||||
super(message)
|
super(message);
|
||||||
this.name = 'OnboardingError'
|
this.name = 'OnboardingError';
|
||||||
this.status = status
|
this.status = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function nowPlusMinutes(minutes: number): Date {
|
function nowPlusMinutes(minutes: number): Date {
|
||||||
const date = new Date()
|
const date = new Date();
|
||||||
date.setMinutes(date.getMinutes() + minutes)
|
date.setMinutes(date.getMinutes() + minutes);
|
||||||
return date
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeEmail(email: string): string {
|
function normalizeEmail(email: string): string {
|
||||||
return email.trim().toLowerCase()
|
return email.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOtpCode(): string {
|
function createOtpCode(): string {
|
||||||
return randomInt(0, 10 ** OTP_LENGTH).toString().padStart(OTP_LENGTH, '0')
|
return randomInt(0, 10 ** OTP_LENGTH)
|
||||||
|
.toString()
|
||||||
|
.padStart(OTP_LENGTH, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashValue(value: string): string {
|
function hashValue(value: string): string {
|
||||||
return createHash('sha256').update(value).digest('hex')
|
return createHash('sha256').update(value).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRemainingAttempts(otpAttempts: number): number {
|
function getRemainingAttempts(otpAttempts: number): number {
|
||||||
return Math.max(0, OTP_MAX_ATTEMPTS - otpAttempts)
|
return Math.max(0, OTP_MAX_ATTEMPTS - otpAttempts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCooldownSeconds(otpLastSentAt: Date): number {
|
function getCooldownSeconds(otpLastSentAt: Date): number {
|
||||||
const elapsedMs = Date.now() - otpLastSentAt.getTime()
|
const elapsedMs = Date.now() - otpLastSentAt.getTime();
|
||||||
const remainingMs = OTP_RESEND_COOLDOWN_SECONDS * 1000 - elapsedMs
|
const remainingMs = OTP_RESEND_COOLDOWN_SECONDS * 1000 - elapsedMs;
|
||||||
return Math.max(0, Math.ceil(remainingMs / 1000))
|
return Math.max(0, Math.ceil(remainingMs / 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
function slugify(value: string): string {
|
function slugify(value: string): string {
|
||||||
@@ -91,28 +90,28 @@ function slugify(value: string): string {
|
|||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
.replace(/-+/g, '-')
|
.replace(/-+/g, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildUniqueComplexSlug(complexName: string): Promise<string> {
|
async function buildUniqueComplexSlug(complexName: string): Promise<string> {
|
||||||
const base = slugify(complexName)
|
const base = slugify(complexName);
|
||||||
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`
|
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`;
|
||||||
|
|
||||||
let candidate = fallback
|
let candidate = fallback;
|
||||||
let index = 1
|
let index = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const existing = await db.complex.findFirst({
|
const existing = await db.complex.findFirst({
|
||||||
where: { complexSlug: candidate },
|
where: { complexSlug: candidate },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return candidate
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
index += 1
|
index += 1;
|
||||||
candidate = `${fallback}-${index}`
|
candidate = `${fallback}-${index}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,98 +121,14 @@ async function sendOtpEmail(email: string, otpCode: string) {
|
|||||||
subject: 'Codigo OTP para validar tu email',
|
subject: 'Codigo OTP para validar tu email',
|
||||||
text: `Tu codigo OTP es ${otpCode}. Vence en ${OTP_TTL_MINUTES} minutos.`,
|
text: `Tu codigo OTP es ${otpCode}. Vence en ${OTP_TTL_MINUTES} minutos.`,
|
||||||
html: `<p>Tu codigo OTP es <strong>${otpCode}</strong>.</p><p>Vence en ${OTP_TTL_MINUTES} minutos.</p>`,
|
html: `<p>Tu codigo OTP es <strong>${otpCode}</strong>.</p><p>Vence en ${OTP_TTL_MINUTES} minutos.</p>`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findSupabaseUserByEmail(
|
export async function startOnboarding(input: OnboardingStartInput): Promise<StartOnboardingResult> {
|
||||||
email: string,
|
const email = normalizeEmail(input.email);
|
||||||
): Promise<SupabaseAuthUser | null> {
|
const otpCode = createOtpCode();
|
||||||
let page = 1
|
const expiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
const perPage = 100
|
const now = new Date();
|
||||||
|
|
||||||
while (page <= 10) {
|
|
||||||
const { data, error } = await getSupabaseAdminClient().auth.admin.listUsers({
|
|
||||||
page,
|
|
||||||
perPage,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(`No se pudo consultar usuarios en Supabase: ${error.message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const found = data.users.find(
|
|
||||||
(user) => user.email?.toLowerCase() === email.toLowerCase(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.users.length < perPage) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
page += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ensureSupabaseUser(params: {
|
|
||||||
email: string
|
|
||||||
fullName: string
|
|
||||||
password: string
|
|
||||||
}): Promise<string> {
|
|
||||||
const existingUser = await findSupabaseUserByEmail(params.email)
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
const { error } = await getSupabaseAdminClient().auth.admin.updateUserById(
|
|
||||||
existingUser.id,
|
|
||||||
{
|
|
||||||
password: params.password,
|
|
||||||
email_confirm: true,
|
|
||||||
user_metadata: {
|
|
||||||
full_name: params.fullName,
|
|
||||||
name: params.fullName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw new Error(
|
|
||||||
`No se pudo actualizar usuario existente en Supabase: ${error.message}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingUser.id
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, error } = await getSupabaseAdminClient().auth.admin.createUser({
|
|
||||||
email: params.email,
|
|
||||||
password: params.password,
|
|
||||||
email_confirm: true,
|
|
||||||
user_metadata: {
|
|
||||||
full_name: params.fullName,
|
|
||||||
name: params.fullName,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (error || !data.user) {
|
|
||||||
throw new Error(
|
|
||||||
`No se pudo crear usuario en Supabase: ${error?.message ?? 'unknown error'}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.user.id
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startOnboarding(
|
|
||||||
input: OnboardingStartInput,
|
|
||||||
): Promise<StartOnboardingResult> {
|
|
||||||
const email = normalizeEmail(input.email)
|
|
||||||
const otpCode = createOtpCode()
|
|
||||||
const expiresAt = nowPlusMinutes(OTP_TTL_MINUTES)
|
|
||||||
const now = new Date()
|
|
||||||
|
|
||||||
const request = await db.onboardingRequest.create({
|
const request = await db.onboardingRequest.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -231,9 +146,9 @@ export async function startOnboarding(
|
|||||||
email: true,
|
email: true,
|
||||||
otpExpiresAt: true,
|
otpExpiresAt: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
await sendOtpEmail(email, otpCode)
|
await sendOtpEmail(email, otpCode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: 'Te enviamos un codigo OTP para validar tu direccion.',
|
message: 'Te enviamos un codigo OTP para validar tu direccion.',
|
||||||
@@ -241,15 +156,15 @@ export async function startOnboarding(
|
|||||||
email: request.email,
|
email: request.email,
|
||||||
expiresAt: request.otpExpiresAt.toISOString(),
|
expiresAt: request.otpExpiresAt.toISOString(),
|
||||||
cooldownSeconds: OTP_RESEND_COOLDOWN_SECONDS,
|
cooldownSeconds: OTP_RESEND_COOLDOWN_SECONDS,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyOnboardingOtp(
|
export async function verifyOnboardingOtp(
|
||||||
input: OnboardingVerifyOtpInput,
|
input: OnboardingVerifyOtpInput
|
||||||
): Promise<VerifyOtpResult> {
|
): Promise<VerifyOtpResult> {
|
||||||
const request = await db.onboardingRequest.findUnique({
|
const request = await db.onboardingRequest.findUnique({
|
||||||
where: { id: input.requestId },
|
where: { id: input.requestId },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!request) {
|
if (!request) {
|
||||||
return {
|
return {
|
||||||
@@ -260,7 +175,7 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
remainingAttempts: 0,
|
remainingAttempts: 0,
|
||||||
cooldownSeconds: 0,
|
cooldownSeconds: 0,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.completedAt) {
|
if (request.completedAt) {
|
||||||
@@ -272,7 +187,7 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: request.otpExpiresAt.toISOString(),
|
expiresAt: request.otpExpiresAt.toISOString(),
|
||||||
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
||||||
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.otpExpiresAt < new Date()) {
|
if (request.otpExpiresAt < new Date()) {
|
||||||
@@ -284,7 +199,7 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: request.otpExpiresAt.toISOString(),
|
expiresAt: request.otpExpiresAt.toISOString(),
|
||||||
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
||||||
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.otpAttempts >= OTP_MAX_ATTEMPTS) {
|
if (request.otpAttempts >= OTP_MAX_ATTEMPTS) {
|
||||||
@@ -296,10 +211,10 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: request.otpExpiresAt.toISOString(),
|
expiresAt: request.otpExpiresAt.toISOString(),
|
||||||
remainingAttempts: 0,
|
remainingAttempts: 0,
|
||||||
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const otpMatches = hashValue(input.otp) === request.otpHash
|
const otpMatches = hashValue(input.otp) === request.otpHash;
|
||||||
|
|
||||||
if (!otpMatches) {
|
if (!otpMatches) {
|
||||||
const updated = await db.onboardingRequest.update({
|
const updated = await db.onboardingRequest.update({
|
||||||
@@ -316,9 +231,9 @@ export async function verifyOnboardingOtp(
|
|||||||
otpExpiresAt: true,
|
otpExpiresAt: true,
|
||||||
otpLastSentAt: true,
|
otpLastSentAt: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const remainingAttempts = getRemainingAttempts(updated.otpAttempts)
|
const remainingAttempts = getRemainingAttempts(updated.otpAttempts);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message:
|
message:
|
||||||
@@ -331,14 +246,14 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: updated.otpExpiresAt.toISOString(),
|
expiresAt: updated.otpExpiresAt.toISOString(),
|
||||||
remainingAttempts,
|
remainingAttempts,
|
||||||
cooldownSeconds: getCooldownSeconds(updated.otpLastSentAt),
|
cooldownSeconds: getCooldownSeconds(updated.otpLastSentAt),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.emailVerifiedAt) {
|
if (!request.emailVerifiedAt) {
|
||||||
await db.onboardingRequest.update({
|
await db.onboardingRequest.update({
|
||||||
where: { id: request.id },
|
where: { id: request.id },
|
||||||
data: { emailVerifiedAt: new Date() },
|
data: { emailVerifiedAt: new Date() },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -349,39 +264,36 @@ export async function verifyOnboardingOtp(
|
|||||||
expiresAt: request.otpExpiresAt.toISOString(),
|
expiresAt: request.otpExpiresAt.toISOString(),
|
||||||
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
remainingAttempts: getRemainingAttempts(request.otpAttempts),
|
||||||
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
cooldownSeconds: getCooldownSeconds(request.otpLastSentAt),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resendOnboardingOtp(
|
export async function resendOnboardingOtp(
|
||||||
input: OnboardingResendOtpInput,
|
input: OnboardingResendOtpInput
|
||||||
): Promise<ResendOtpResult> {
|
): Promise<ResendOtpResult> {
|
||||||
const request = await db.onboardingRequest.findUnique({
|
const request = await db.onboardingRequest.findUnique({
|
||||||
where: { id: input.requestId },
|
where: { id: input.requestId },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!request) {
|
if (!request) {
|
||||||
throw new OnboardingError('Solicitud de onboarding invalida o expirada.', 404)
|
throw new OnboardingError('Solicitud de onboarding invalida o expirada.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.completedAt) {
|
if (request.completedAt) {
|
||||||
throw new OnboardingError('Este onboarding ya fue completado.', 400)
|
throw new OnboardingError('Este onboarding ya fue completado.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.emailVerifiedAt) {
|
if (request.emailVerifiedAt) {
|
||||||
throw new OnboardingError('El email ya fue verificado.', 400)
|
throw new OnboardingError('El email ya fue verificado.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cooldownSeconds = getCooldownSeconds(request.otpLastSentAt)
|
const cooldownSeconds = getCooldownSeconds(request.otpLastSentAt);
|
||||||
if (cooldownSeconds > 0) {
|
if (cooldownSeconds > 0) {
|
||||||
throw new OnboardingError(
|
throw new OnboardingError(`Debes esperar ${cooldownSeconds}s antes de reenviar el OTP.`, 429);
|
||||||
`Debes esperar ${cooldownSeconds}s antes de reenviar el OTP.`,
|
|
||||||
429,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const otpCode = createOtpCode()
|
const otpCode = createOtpCode();
|
||||||
const expiresAt = nowPlusMinutes(OTP_TTL_MINUTES)
|
const expiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
const now = new Date()
|
const now = new Date();
|
||||||
|
|
||||||
const updated = await db.onboardingRequest.update({
|
const updated = await db.onboardingRequest.update({
|
||||||
where: { id: request.id },
|
where: { id: request.id },
|
||||||
@@ -400,9 +312,9 @@ export async function resendOnboardingOtp(
|
|||||||
otpExpiresAt: true,
|
otpExpiresAt: true,
|
||||||
otpAttempts: true,
|
otpAttempts: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
await sendOtpEmail(updated.email, otpCode)
|
await sendOtpEmail(updated.email, otpCode);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: 'Te enviamos un nuevo codigo OTP.',
|
message: 'Te enviamos un nuevo codigo OTP.',
|
||||||
@@ -411,100 +323,114 @@ export async function resendOnboardingOtp(
|
|||||||
expiresAt: updated.otpExpiresAt.toISOString(),
|
expiresAt: updated.otpExpiresAt.toISOString(),
|
||||||
cooldownSeconds: OTP_RESEND_COOLDOWN_SECONDS,
|
cooldownSeconds: OTP_RESEND_COOLDOWN_SECONDS,
|
||||||
remainingAttempts: getRemainingAttempts(updated.otpAttempts),
|
remainingAttempts: getRemainingAttempts(updated.otpAttempts),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function completeOnboarding(input: OnboardingCompleteInput) {
|
export async function completeOnboarding(input: OnboardingCompleteInput) {
|
||||||
const onboardingRequest = await db.onboardingRequest.findUnique({
|
const onboardingRequest = await db.onboardingRequest.findUnique({
|
||||||
where: { id: input.onboardingRequestId },
|
where: { id: input.onboardingRequestId },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!onboardingRequest) {
|
if (!onboardingRequest) {
|
||||||
throw new OnboardingError('Solicitud de onboarding invalida o expirada.', 400)
|
throw new OnboardingError('Solicitud de onboarding invalida o expirada.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onboardingRequest.otpExpiresAt < new Date()) {
|
if (onboardingRequest.otpExpiresAt < new Date()) {
|
||||||
throw new OnboardingError(
|
throw new OnboardingError('La sesion de onboarding expiro. Solicita un nuevo OTP.', 400);
|
||||||
'La sesion de onboarding expiro. Solicita un nuevo OTP.',
|
|
||||||
400,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!onboardingRequest.emailVerifiedAt) {
|
if (!onboardingRequest.emailVerifiedAt) {
|
||||||
throw new OnboardingError('Debes verificar el email antes de continuar.', 400)
|
throw new OnboardingError('Debes verificar el email antes de continuar.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onboardingRequest.completedAt) {
|
if (onboardingRequest.completedAt) {
|
||||||
throw new OnboardingError('Este onboarding ya fue completado.', 400)
|
throw new OnboardingError('Este onboarding ya fue completado.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const plan = await db.plan.findUnique({
|
const plan = await db.plan.findUnique({
|
||||||
where: { code: input.planCode },
|
where: { code: input.planCode },
|
||||||
select: { code: true },
|
select: { code: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!plan) {
|
if (!plan) {
|
||||||
throw new OnboardingError('El plan seleccionado no existe.', 400)
|
throw new OnboardingError('El plan seleccionado no existe.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const supabaseUserId = await ensureSupabaseUser({
|
const complexSlug = await buildUniqueComplexSlug(input.complexName);
|
||||||
email: onboardingRequest.email,
|
|
||||||
fullName: onboardingRequest.fullName,
|
|
||||||
password: input.password,
|
|
||||||
})
|
|
||||||
|
|
||||||
const complexSlug = await buildUniqueComplexSlug(input.complexName)
|
|
||||||
|
|
||||||
const result = await db.$transaction(async (tx) => {
|
const result = await db.$transaction(async (tx) => {
|
||||||
|
const existingUser = await tx.user.findUnique({
|
||||||
|
where: { email: onboardingRequest.email },
|
||||||
|
});
|
||||||
|
|
||||||
|
let userId: string;
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
userId = existingUser.id;
|
||||||
|
} else {
|
||||||
|
const newUser = await auth.api.signUpEmail({
|
||||||
|
body: {
|
||||||
|
email: onboardingRequest.email,
|
||||||
|
password: input.password,
|
||||||
|
name: onboardingRequest.fullName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newUser.user) {
|
||||||
|
throw new OnboardingError('No se pudo crear el usuario en Better Auth.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
userId = newUser.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
const user = await tx.user.upsert({
|
const user = await tx.user.upsert({
|
||||||
where: { email: onboardingRequest.email },
|
where: { email: onboardingRequest.email },
|
||||||
update: {
|
update: {
|
||||||
fullName: onboardingRequest.fullName,
|
fullName: onboardingRequest.fullName,
|
||||||
supabaseUserId,
|
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: uuidv7(),
|
id: userId,
|
||||||
fullName: onboardingRequest.fullName,
|
fullName: onboardingRequest.fullName,
|
||||||
email: onboardingRequest.email,
|
email: onboardingRequest.email,
|
||||||
supabaseUserId,
|
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const complex = await tx.complex.create({
|
const complex = await tx.complex.create({
|
||||||
data: {
|
data: {
|
||||||
id: uuidv7(),
|
id: uuidv7(),
|
||||||
complexName: input.complexName.trim(),
|
complexName: input.complexName.trim(),
|
||||||
physicalAddress: input.physicalAddress.trim(),
|
physicalAddress: input.physicalAddress.trim(),
|
||||||
|
city: input.city?.trim() || null,
|
||||||
|
state: input.state?.trim() || null,
|
||||||
|
country: input.country?.trim() || null,
|
||||||
complexSlug,
|
complexSlug,
|
||||||
adminEmail: onboardingRequest.email,
|
adminEmail: onboardingRequest.email,
|
||||||
planCode: input.planCode,
|
planCode: input.planCode,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
await tx.complexUser.create({
|
await tx.complexUser.create({
|
||||||
data: {
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
complexId: complex.id,
|
complexId: complex.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
role: 'ADMIN',
|
role: 'ADMIN',
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
await tx.onboardingRequest.update({
|
await tx.onboardingRequest.update({
|
||||||
where: { id: onboardingRequest.id },
|
where: { id: onboardingRequest.id },
|
||||||
data: {
|
data: {
|
||||||
completedAt: new Date(),
|
completedAt: new Date(),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
complexId: complex.id,
|
complexId: complex.id,
|
||||||
complexSlug: complex.complexSlug,
|
complexSlug: complex.complexSlug,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
...result,
|
|
||||||
supabaseUserId,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { resendPasswordResetOtp } from '@/modules/password-reset/services/password-reset.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { PasswordResetResendInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
export async function resendPasswordResetHandler(c: AppContext) {
|
||||||
|
const payload = c.req.valid('json' as never) as PasswordResetResendInput;
|
||||||
|
|
||||||
|
const result = await resendPasswordResetOtp(payload);
|
||||||
|
return c.json(result, 202);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { startPasswordReset } from '@/modules/password-reset/services/password-reset.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { PasswordResetStartInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
export async function startPasswordResetHandler(c: AppContext) {
|
||||||
|
const payload = c.req.valid('json' as never) as PasswordResetStartInput;
|
||||||
|
|
||||||
|
const result = await startPasswordReset(payload);
|
||||||
|
return c.json(result, 202);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { verifyOtpAndResetPassword } from '@/modules/password-reset/services/password-reset.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { PasswordResetVerifyInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
export async function verifyPasswordResetHandler(c: AppContext) {
|
||||||
|
const payload = c.req.valid('json' as never) as PasswordResetVerifyInput;
|
||||||
|
|
||||||
|
const ip = c.req.header('x-forwarded-for') || c.req.header('cf-connecting-ip') || 'unknown';
|
||||||
|
const userAgent = c.req.header('user-agent');
|
||||||
|
|
||||||
|
const result = await verifyOtpAndResetPassword(payload, ip, userAgent);
|
||||||
|
return c.json(result, 200);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { resendPasswordResetHandler } from '@/modules/password-reset/handlers/resend-password-reset.handler';
|
||||||
|
import { startPasswordResetHandler } from '@/modules/password-reset/handlers/start-password-reset.handler';
|
||||||
|
import { verifyPasswordResetHandler } from '@/modules/password-reset/handlers/verify-password-reset.handler';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import {
|
||||||
|
passwordResetResendSchema,
|
||||||
|
passwordResetStartSchema,
|
||||||
|
passwordResetVerifySchema,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
|
export const passwordResetRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
|
passwordResetRoutes.post(
|
||||||
|
'/start',
|
||||||
|
zValidator('json', passwordResetStartSchema),
|
||||||
|
startPasswordResetHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
passwordResetRoutes.post(
|
||||||
|
'/verify',
|
||||||
|
zValidator('json', passwordResetVerifySchema),
|
||||||
|
verifyPasswordResetHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
passwordResetRoutes.post(
|
||||||
|
'/resend',
|
||||||
|
zValidator('json', passwordResetResendSchema),
|
||||||
|
resendPasswordResetHandler
|
||||||
|
);
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
import { createHash, randomInt } from 'node:crypto';
|
||||||
|
import { auth } from '@/lib/auth';
|
||||||
|
import { sendMail } from '@/lib/mailer';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import type {
|
||||||
|
PasswordResetResendInput,
|
||||||
|
PasswordResetStartInput,
|
||||||
|
PasswordResetVerifyInput,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
|
const OTP_LENGTH = 6;
|
||||||
|
const OTP_TTL_MINUTES = Number(Bun.env.PASSWORD_RESET_OTP_TTL_MINUTES ?? 10);
|
||||||
|
const OTP_MAX_ATTEMPTS = Number(Bun.env.PASSWORD_RESET_OTP_MAX_ATTEMPTS ?? 5);
|
||||||
|
const OTP_RESEND_COOLDOWN_SECONDS = Number(
|
||||||
|
Bun.env.PASSWORD_RESET_OTP_RESEND_COOLDOWN_SECONDS ?? 30
|
||||||
|
);
|
||||||
|
|
||||||
|
export type PasswordResetStartResult = {
|
||||||
|
message: string;
|
||||||
|
requestId: string;
|
||||||
|
email: string;
|
||||||
|
expiresAt: string;
|
||||||
|
cooldownSeconds: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PasswordResetVerifyResult = {
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PasswordResetResendResult = {
|
||||||
|
message: string;
|
||||||
|
requestId: string;
|
||||||
|
email: string;
|
||||||
|
expiresAt: string;
|
||||||
|
cooldownSeconds: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PasswordResetError extends Error {
|
||||||
|
status: 400 | 404 | 409 | 429;
|
||||||
|
|
||||||
|
constructor(message: string, status: 400 | 404 | 409 | 429 = 400) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'PasswordResetError';
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nowPlusMinutes(minutes: number): Date {
|
||||||
|
const date = new Date();
|
||||||
|
date.setMinutes(date.getMinutes() + minutes);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEmail(email: string): string {
|
||||||
|
return email.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOtpCode(): string {
|
||||||
|
return randomInt(0, 10 ** OTP_LENGTH)
|
||||||
|
.toString()
|
||||||
|
.padStart(OTP_LENGTH, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hashValue(value: string): string {
|
||||||
|
return createHash('sha256').update(value).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRemainingAttempts(otpAttempts: number): number {
|
||||||
|
return Math.max(0, OTP_MAX_ATTEMPTS - otpAttempts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCooldownSeconds(otpLastSentAt: Date): number {
|
||||||
|
const elapsedMs = Date.now() - otpLastSentAt.getTime();
|
||||||
|
const remainingMs = OTP_RESEND_COOLDOWN_SECONDS * 1000 - elapsedMs;
|
||||||
|
return Math.max(0, Math.ceil(remainingMs / 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAgentInfo = {
|
||||||
|
browser: string;
|
||||||
|
os: string;
|
||||||
|
device: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type IpGeoInfo = {
|
||||||
|
ip: string;
|
||||||
|
city: string;
|
||||||
|
country: string;
|
||||||
|
countryCode: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseUserAgent(ua: string | undefined): UserAgentInfo {
|
||||||
|
if (!ua) {
|
||||||
|
return { browser: 'Desconocido', os: 'Desconocido', device: 'Desconocido' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const uaLower = ua.toLowerCase();
|
||||||
|
|
||||||
|
let browser = 'Desconocido';
|
||||||
|
if (uaLower.includes('chrome')) browser = 'Chrome';
|
||||||
|
else if (uaLower.includes('firefox')) browser = 'Firefox';
|
||||||
|
else if (uaLower.includes('safari')) browser = 'Safari';
|
||||||
|
else if (uaLower.includes('edge')) browser = 'Edge';
|
||||||
|
else if (uaLower.includes('opera')) browser = 'Opera';
|
||||||
|
|
||||||
|
let os = 'Desconocido';
|
||||||
|
if (uaLower.includes('windows')) os = 'Windows';
|
||||||
|
else if (uaLower.includes('mac')) os = 'macOS';
|
||||||
|
else if (uaLower.includes('linux')) os = 'Linux';
|
||||||
|
else if (uaLower.includes('android')) os = 'Android';
|
||||||
|
else if (uaLower.includes('ios') || uaLower.includes('iphone') || uaLower.includes('ipad'))
|
||||||
|
os = 'iOS';
|
||||||
|
|
||||||
|
let device = 'Desktop';
|
||||||
|
if (uaLower.includes('mobile') || uaLower.includes('android')) device = 'Móvil';
|
||||||
|
else if (uaLower.includes('tablet') || uaLower.includes('ipad')) device = 'Tablet';
|
||||||
|
|
||||||
|
return { browser, os, device };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchGeoInfo(ip: string): Promise<IpGeoInfo | null> {
|
||||||
|
if (ip === '127.0.0.1' || ip === '::1' || ip.startsWith('192.168.') || ip.startsWith('10.')) {
|
||||||
|
return { ip, city: 'Red local', country: 'Red local', countryCode: 'LOCAL' };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`http://ip-api.com/json/${ip}?fields=status,country,countryCode,city,query`
|
||||||
|
);
|
||||||
|
if (!response.ok) return null;
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.status !== 'success') return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
ip: data.query,
|
||||||
|
city: data.city || 'Desconocida',
|
||||||
|
country: data.country || 'Desconocido',
|
||||||
|
countryCode: data.countryCode || '',
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendResetOtpEmail(email: string, otpCode: string) {
|
||||||
|
await sendMail({
|
||||||
|
to: email,
|
||||||
|
subject: 'Código para restablecer tu contraseña',
|
||||||
|
text: `Tu código OTP es ${otpCode}. Vence en ${OTP_TTL_MINUTES} minutos.`,
|
||||||
|
html: `<p>Tu código OTP es <strong>${otpCode}</strong>.</p><p>Vence en ${OTP_TTL_MINUTES} minutos.</p>`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendPasswordChangedEmail(
|
||||||
|
email: string,
|
||||||
|
ipInfo: IpGeoInfo | null,
|
||||||
|
userAgent: string | undefined
|
||||||
|
) {
|
||||||
|
const deviceInfo = parseUserAgent(userAgent);
|
||||||
|
const now = new Date();
|
||||||
|
const formattedDate = now.toLocaleString('es-AR', {
|
||||||
|
timeZone: 'America/Argentina/Buenos_Aires',
|
||||||
|
dateStyle: 'full',
|
||||||
|
timeStyle: 'short',
|
||||||
|
});
|
||||||
|
|
||||||
|
const location = ipInfo
|
||||||
|
? `${ipInfo.city}${ipInfo.city !== 'Red local' ? `, ${ipInfo.country}` : ''}`
|
||||||
|
: 'No disponible';
|
||||||
|
const ip = ipInfo?.ip || 'No disponible';
|
||||||
|
|
||||||
|
await sendMail({
|
||||||
|
to: email,
|
||||||
|
subject: 'Tu contraseña ha sido cambiada',
|
||||||
|
text: `Tu contraseña fue cambiada el ${formattedDate}. Si no fuiste vos, contactanos inmediatamente.`,
|
||||||
|
html: `
|
||||||
|
<h2>Tu contraseña ha sido cambiada</h2>
|
||||||
|
<p>Se ha modificado la contraseña de tu cuenta.</p>
|
||||||
|
<h3>Detalles del cambio:</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Fecha y hora:</strong> ${formattedDate}</li>
|
||||||
|
<li><strong>Dirección IP:</strong> ${ip}</li>
|
||||||
|
<li><strong>Ubicación:</strong> ${location}</li>
|
||||||
|
<li><strong>Dispositivo:</strong> ${deviceInfo.device} (${deviceInfo.os} - ${deviceInfo.browser})</li>
|
||||||
|
</ul>
|
||||||
|
<p>Si no fuiste vos, contactanos inmediatamente.</p>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startPasswordReset(
|
||||||
|
input: PasswordResetStartInput
|
||||||
|
): Promise<PasswordResetStartResult> {
|
||||||
|
const email = normalizeEmail(input.email);
|
||||||
|
|
||||||
|
const existingUser = await db.user.findUnique({
|
||||||
|
where: { email },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
return {
|
||||||
|
message: 'Si el email existe, recibirás un código de verificación.',
|
||||||
|
requestId: uuidv7(),
|
||||||
|
email,
|
||||||
|
expiresAt: new Date().toISOString(),
|
||||||
|
cooldownSeconds: OTP_RESEND_COOLDOWN_SECONDS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingRequest = await db.passwordResetRequest.findFirst({
|
||||||
|
where: { email, usedAt: null },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
let requestId: string;
|
||||||
|
let otpCode: string;
|
||||||
|
let otpHash: string;
|
||||||
|
let otpExpiresAt: Date;
|
||||||
|
let cooldownSeconds = 0;
|
||||||
|
|
||||||
|
if (existingRequest) {
|
||||||
|
requestId = existingRequest.id;
|
||||||
|
otpExpiresAt = new Date(existingRequest.otpExpiresAt);
|
||||||
|
|
||||||
|
if (new Date() > otpExpiresAt) {
|
||||||
|
otpCode = createOtpCode();
|
||||||
|
otpHash = hashValue(otpCode);
|
||||||
|
otpExpiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
|
|
||||||
|
await db.passwordResetRequest.update({
|
||||||
|
where: { id: requestId },
|
||||||
|
data: {
|
||||||
|
otpHash,
|
||||||
|
otpExpiresAt,
|
||||||
|
otpAttempts: 0,
|
||||||
|
otpLastSentAt: new Date(),
|
||||||
|
otpResendCount: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cooldownSeconds = getCooldownSeconds(new Date(existingRequest.otpLastSentAt));
|
||||||
|
|
||||||
|
if (cooldownSeconds > 0) {
|
||||||
|
return {
|
||||||
|
message: `Debes esperar ${cooldownSeconds} segundos antes de reenviar.`,
|
||||||
|
requestId,
|
||||||
|
email,
|
||||||
|
expiresAt: otpExpiresAt.toISOString(),
|
||||||
|
cooldownSeconds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
otpCode = createOtpCode();
|
||||||
|
otpHash = hashValue(otpCode);
|
||||||
|
otpExpiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
|
|
||||||
|
await db.passwordResetRequest.update({
|
||||||
|
where: { id: requestId },
|
||||||
|
data: {
|
||||||
|
otpHash,
|
||||||
|
otpExpiresAt,
|
||||||
|
otpAttempts: 0,
|
||||||
|
otpLastSentAt: new Date(),
|
||||||
|
otpResendCount: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendResetOtpEmail(email, otpCode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Código reenviado.',
|
||||||
|
requestId,
|
||||||
|
email,
|
||||||
|
expiresAt: otpExpiresAt.toISOString(),
|
||||||
|
cooldownSeconds: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestId = uuidv7();
|
||||||
|
otpCode = createOtpCode();
|
||||||
|
otpHash = hashValue(otpCode);
|
||||||
|
otpExpiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
|
|
||||||
|
await db.passwordResetRequest.create({
|
||||||
|
data: {
|
||||||
|
id: requestId,
|
||||||
|
email,
|
||||||
|
otpHash,
|
||||||
|
otpExpiresAt,
|
||||||
|
otpLastSentAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendResetOtpEmail(email, otpCode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Código enviado successfully.',
|
||||||
|
requestId,
|
||||||
|
email,
|
||||||
|
expiresAt: otpExpiresAt.toISOString(),
|
||||||
|
cooldownSeconds: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyOtpAndResetPassword(
|
||||||
|
input: PasswordResetVerifyInput,
|
||||||
|
ip: string | undefined,
|
||||||
|
userAgent: string | undefined
|
||||||
|
): Promise<PasswordResetVerifyResult> {
|
||||||
|
const { requestId, otp, newPassword } = input;
|
||||||
|
|
||||||
|
const request = await db.passwordResetRequest.findUnique({
|
||||||
|
where: { id: requestId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
throw new PasswordResetError('Solicitud inválida.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.usedAt) {
|
||||||
|
throw new PasswordResetError('Esta solicitud ya fue utilizada.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new Date() > new Date(request.otpExpiresAt)) {
|
||||||
|
throw new PasswordResetError('El código ha vencido.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remainingAttempts = getRemainingAttempts(request.otpAttempts);
|
||||||
|
if (remainingAttempts <= 0) {
|
||||||
|
throw new PasswordResetError('Superaste la cantidad maxima de intentos.', 429);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otpHash = hashValue(otp);
|
||||||
|
if (otpHash !== request.otpHash) {
|
||||||
|
await db.passwordResetRequest.update({
|
||||||
|
where: { id: requestId },
|
||||||
|
data: { otpAttempts: { increment: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new PasswordResetError('Código incorrecto.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUser = await db.user.findUnique({
|
||||||
|
where: { email: request.email },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new PasswordResetError('Usuario no encontrado.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await auth.api.changePassword({
|
||||||
|
userId: existingUser.id,
|
||||||
|
password: newPassword,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.passwordResetRequest.update({
|
||||||
|
where: { id: requestId },
|
||||||
|
data: { usedAt: new Date() },
|
||||||
|
});
|
||||||
|
|
||||||
|
const geoInfo = await fetchGeoInfo(ip || 'unknown');
|
||||||
|
await sendPasswordChangedEmail(request.email, geoInfo, userAgent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Contraseña restablecida correctamente.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resendPasswordResetOtp(
|
||||||
|
input: PasswordResetResendInput
|
||||||
|
): Promise<PasswordResetResendResult> {
|
||||||
|
const { requestId } = input;
|
||||||
|
|
||||||
|
const request = await db.passwordResetRequest.findUnique({
|
||||||
|
where: { id: requestId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
throw new PasswordResetError('Solicitud inválida.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.usedAt) {
|
||||||
|
throw new PasswordResetError('Esta solicitud ya fue utilizada.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cooldownSeconds = getCooldownSeconds(new Date(request.otpLastSentAt));
|
||||||
|
if (cooldownSeconds > 0) {
|
||||||
|
throw new PasswordResetError(
|
||||||
|
`Debes esperar ${cooldownSeconds} segundos antes de reenviar.`,
|
||||||
|
429
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const otpCode = createOtpCode();
|
||||||
|
const otpHash = hashValue(otpCode);
|
||||||
|
const otpExpiresAt = nowPlusMinutes(OTP_TTL_MINUTES);
|
||||||
|
|
||||||
|
await db.passwordResetRequest.update({
|
||||||
|
where: { id: requestId },
|
||||||
|
data: {
|
||||||
|
otpHash,
|
||||||
|
otpExpiresAt,
|
||||||
|
otpAttempts: 0,
|
||||||
|
otpLastSentAt: new Date(),
|
||||||
|
otpResendCount: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendResetOtpEmail(request.email, otpCode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Código reenviado.',
|
||||||
|
requestId,
|
||||||
|
email: request.email,
|
||||||
|
expiresAt: otpExpiresAt.toISOString(),
|
||||||
|
cooldownSeconds: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { db } from '@/lib/prisma';
|
||||||
import { db } from '@/lib/prisma'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
export async function listPlansHandler(c: AppContext) {
|
export async function listPlansHandler(c: AppContext) {
|
||||||
const plans = await db.plan.findMany({
|
const plans = await db.plan.findMany({
|
||||||
@@ -11,13 +11,13 @@ export async function listPlansHandler(c: AppContext) {
|
|||||||
orderBy: {
|
orderBy: {
|
||||||
price: 'asc',
|
price: 'asc',
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return c.json(
|
return c.json(
|
||||||
plans.map((plan) => ({
|
plans.map((plan) => ({
|
||||||
code: plan.code,
|
code: plan.code,
|
||||||
name: plan.name,
|
name: plan.name,
|
||||||
price: Number(plan.price),
|
price: Number(plan.price),
|
||||||
})),
|
}))
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Hono } from 'hono'
|
import { listPlansHandler } from '@/modules/plan/handlers/list-plans.handler';
|
||||||
import { listPlansHandler } from '@/modules/plan/handlers/list-plans.handler'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
export const planRoutes = new Hono<AppEnv>()
|
export const planRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
planRoutes.get('/', listPlansHandler)
|
planRoutes.get('/', listPlansHandler);
|
||||||
|
|||||||
@@ -1,44 +1,41 @@
|
|||||||
import type { PlanRules } from '@repo/api-contract'
|
import type { PlanRules } from '@repo/api-contract';
|
||||||
import { planRulesSchema } from '@repo/api-contract'
|
import { planRulesSchema } from '@repo/api-contract';
|
||||||
|
|
||||||
export type PlanUsageSnapshot = {
|
export type PlanUsageSnapshot = {
|
||||||
courtsCount: number
|
courtsCount: number;
|
||||||
bookingsToday: number
|
bookingsToday: number;
|
||||||
activeUsersCount?: number
|
activeUsersCount?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type PlanViolationCode =
|
export type PlanViolationCode =
|
||||||
| 'MAX_COURTS_REACHED'
|
| 'MAX_COURTS_REACHED'
|
||||||
| 'MAX_BOOKINGS_PER_DAY_REACHED'
|
| 'MAX_BOOKINGS_PER_DAY_REACHED'
|
||||||
| 'MAX_ACTIVE_USERS_REACHED'
|
| 'MAX_ACTIVE_USERS_REACHED';
|
||||||
|
|
||||||
export type PlanViolation = {
|
export type PlanViolation = {
|
||||||
code: PlanViolationCode
|
code: PlanViolationCode;
|
||||||
message: string
|
message: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function parsePlanRules(input: unknown): PlanRules {
|
export function parsePlanRules(input: unknown): PlanRules {
|
||||||
return planRulesSchema.parse(input)
|
return planRulesSchema.parse(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evaluatePlanUsage(
|
export function evaluatePlanUsage(rules: PlanRules, usage: PlanUsageSnapshot): PlanViolation[] {
|
||||||
rules: PlanRules,
|
const violations: PlanViolation[] = [];
|
||||||
usage: PlanUsageSnapshot,
|
|
||||||
): PlanViolation[] {
|
|
||||||
const violations: PlanViolation[] = []
|
|
||||||
|
|
||||||
if (usage.courtsCount >= rules.limits.maxCourts) {
|
if (usage.courtsCount >= rules.limits.maxCourts) {
|
||||||
violations.push({
|
violations.push({
|
||||||
code: 'MAX_COURTS_REACHED',
|
code: 'MAX_COURTS_REACHED',
|
||||||
message: `El plan permite hasta ${rules.limits.maxCourts} canchas.`,
|
message: `El plan permite hasta ${rules.limits.maxCourts} canchas.`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usage.bookingsToday >= rules.limits.maxBookingsPerDay) {
|
if (usage.bookingsToday >= rules.limits.maxBookingsPerDay) {
|
||||||
violations.push({
|
violations.push({
|
||||||
code: 'MAX_BOOKINGS_PER_DAY_REACHED',
|
code: 'MAX_BOOKINGS_PER_DAY_REACHED',
|
||||||
message: `El plan permite hasta ${rules.limits.maxBookingsPerDay} turnos por dia.`,
|
message: `El plan permite hasta ${rules.limits.maxBookingsPerDay} turnos por dia.`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -49,15 +46,12 @@ export function evaluatePlanUsage(
|
|||||||
violations.push({
|
violations.push({
|
||||||
code: 'MAX_ACTIVE_USERS_REACHED',
|
code: 'MAX_ACTIVE_USERS_REACHED',
|
||||||
message: `El plan permite hasta ${rules.limits.maxActiveUsers} usuarios activos.`,
|
message: `El plan permite hasta ${rules.limits.maxActiveUsers} usuarios activos.`,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return violations
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFeatureEnabled(
|
export function isFeatureEnabled(rules: PlanRules, feature: keyof PlanRules['features']): boolean {
|
||||||
rules: PlanRules,
|
return rules.features[feature];
|
||||||
feature: keyof PlanRules['features'],
|
|
||||||
): boolean {
|
|
||||||
return rules.features[feature]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import type { CreatePublicBookingInput } from '@repo/api-contract'
|
|
||||||
import {
|
import {
|
||||||
PublicBookingServiceError,
|
PublicBookingServiceError,
|
||||||
createPublicBooking,
|
createPublicBooking,
|
||||||
} from '@/modules/public-booking/services/public-booking.service'
|
} from '@/modules/public-booking/services/public-booking.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { CreatePublicBookingInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexSlugParams = { complexSlug: string }
|
type ComplexSlugParams = { complexSlug: string };
|
||||||
|
|
||||||
export async function createPublicBookingHandler(c: AppContext) {
|
export async function createPublicBookingHandler(c: AppContext) {
|
||||||
const { complexSlug } = c.req.valid('param' as never) as ComplexSlugParams
|
const { complexSlug } = c.req.valid('param' as never) as ComplexSlugParams;
|
||||||
const payload = c.req.valid('json' as never) as CreatePublicBookingInput
|
const payload = c.req.valid('json' as never) as CreatePublicBookingInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const booking = await createPublicBooking(complexSlug, payload)
|
const booking = await createPublicBooking(complexSlug, payload);
|
||||||
return c.json(booking, 201)
|
return c.json(booking, 201);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof PublicBookingServiceError) {
|
if (error instanceof PublicBookingServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
PublicBookingServiceError,
|
PublicBookingServiceError,
|
||||||
getPublicBookingConfirmation,
|
getPublicBookingConfirmation,
|
||||||
} from '@/modules/public-booking/services/public-booking.service'
|
} from '@/modules/public-booking/services/public-booking.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type BookingCodeParams = {
|
type BookingCodeParams = {
|
||||||
complexSlug: string
|
complexSlug: string;
|
||||||
bookingCode: string
|
bookingCode: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function getPublicBookingConfirmationHandler(c: AppContext) {
|
export async function getPublicBookingConfirmationHandler(c: AppContext) {
|
||||||
const { complexSlug, bookingCode } = c.req.valid('param' as never) as BookingCodeParams
|
const { complexSlug, bookingCode } = c.req.valid('param' as never) as BookingCodeParams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const booking = await getPublicBookingConfirmation(complexSlug, bookingCode)
|
const booking = await getPublicBookingConfirmation(complexSlug, bookingCode);
|
||||||
return c.json(booking)
|
return c.json(booking);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof PublicBookingServiceError) {
|
if (error instanceof PublicBookingServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import type { PublicAvailabilityQuery } from '@repo/api-contract'
|
|
||||||
import {
|
import {
|
||||||
PublicBookingServiceError,
|
PublicBookingServiceError,
|
||||||
listPublicAvailability,
|
listPublicAvailability,
|
||||||
} from '@/modules/public-booking/services/public-booking.service'
|
} from '@/modules/public-booking/services/public-booking.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { PublicAvailabilityQuery } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexSlugParams = { complexSlug: string }
|
type ComplexSlugParams = { complexSlug: string };
|
||||||
|
|
||||||
export async function listPublicAvailabilityHandler(c: AppContext) {
|
export async function listPublicAvailabilityHandler(c: AppContext) {
|
||||||
const { complexSlug } = c.req.valid('param' as never) as ComplexSlugParams
|
const { complexSlug } = c.req.valid('param' as never) as ComplexSlugParams;
|
||||||
const query = c.req.valid('query' as never) as PublicAvailabilityQuery
|
const query = c.req.valid('query' as never) as PublicAvailabilityQuery;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const availability = await listPublicAvailability(complexSlug, query)
|
const availability = await listPublicAvailability(complexSlug, query);
|
||||||
return c.json(availability)
|
return c.json(availability);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof PublicBookingServiceError) {
|
if (error instanceof PublicBookingServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { createPublicBookingHandler } from '@/modules/public-booking/handlers/create-public-booking.handler';
|
||||||
import {
|
import { getPublicBookingConfirmationHandler } from '@/modules/public-booking/handlers/get-public-booking-confirmation.handler';
|
||||||
createPublicBookingSchema,
|
import { listPublicAvailabilityHandler } from '@/modules/public-booking/handlers/list-public-availability.handler';
|
||||||
publicAvailabilityQuerySchema,
|
import type { AppEnv } from '@/types/hono';
|
||||||
} from '@repo/api-contract'
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import { Hono } from 'hono'
|
import { createPublicBookingSchema, publicAvailabilityQuerySchema } from '@repo/api-contract';
|
||||||
import { z } from 'zod'
|
import { Hono } from 'hono';
|
||||||
import { createPublicBookingHandler } from '@/modules/public-booking/handlers/create-public-booking.handler'
|
import { z } from 'zod';
|
||||||
import { getPublicBookingConfirmationHandler } from '@/modules/public-booking/handlers/get-public-booking-confirmation.handler'
|
|
||||||
import { listPublicAvailabilityHandler } from '@/modules/public-booking/handlers/list-public-availability.handler'
|
|
||||||
import type { AppEnv } from '@/types/hono'
|
|
||||||
|
|
||||||
export const publicBookingRoutes = new Hono<AppEnv>()
|
export const publicBookingRoutes = new Hono<AppEnv>();
|
||||||
const complexSlugParamsSchema = z.object({ complexSlug: z.string().trim().min(1) })
|
const complexSlugParamsSchema = z.object({ complexSlug: z.string().trim().min(1) });
|
||||||
const confirmationParamsSchema = z.object({
|
const confirmationParamsSchema = z.object({
|
||||||
complexSlug: z.string().trim().min(1),
|
complexSlug: z.string().trim().min(1),
|
||||||
bookingCode: z.string().trim().min(6).max(8),
|
bookingCode: z.string().trim().min(6).max(8),
|
||||||
})
|
});
|
||||||
|
|
||||||
publicBookingRoutes.get(
|
publicBookingRoutes.get(
|
||||||
'/complex/:complexSlug/availability',
|
'/complex/:complexSlug/availability',
|
||||||
zValidator('param', complexSlugParamsSchema),
|
zValidator('param', complexSlugParamsSchema),
|
||||||
zValidator('query', publicAvailabilityQuerySchema),
|
zValidator('query', publicAvailabilityQuerySchema),
|
||||||
listPublicAvailabilityHandler,
|
listPublicAvailabilityHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
publicBookingRoutes.post(
|
publicBookingRoutes.post(
|
||||||
'/complex/:complexSlug',
|
'/complex/:complexSlug',
|
||||||
zValidator('param', complexSlugParamsSchema),
|
zValidator('param', complexSlugParamsSchema),
|
||||||
zValidator('json', createPublicBookingSchema),
|
zValidator('json', createPublicBookingSchema),
|
||||||
createPublicBookingHandler,
|
createPublicBookingHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
publicBookingRoutes.get(
|
publicBookingRoutes.get(
|
||||||
'/complex/:complexSlug/confirmation/:bookingCode',
|
'/complex/:complexSlug/confirmation/:bookingCode',
|
||||||
zValidator('param', confirmationParamsSchema),
|
zValidator('param', confirmationParamsSchema),
|
||||||
getPublicBookingConfirmationHandler,
|
getPublicBookingConfirmationHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
|
import { randomInt } from 'node:crypto';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service';
|
||||||
import type {
|
import type {
|
||||||
CreatePublicBookingInput,
|
CreatePublicBookingInput,
|
||||||
DayOfWeek,
|
DayOfWeek,
|
||||||
PublicAvailabilityQuery,
|
PublicAvailabilityQuery,
|
||||||
} from '@repo/api-contract'
|
} from '@repo/api-contract';
|
||||||
import { randomInt } from 'node:crypto'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
import { v7 as uuidv7 } from 'uuid'
|
|
||||||
import { db } from '@/lib/prisma'
|
|
||||||
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service'
|
|
||||||
|
|
||||||
type Slot = {
|
type Slot = {
|
||||||
startTime: string
|
startTime: string;
|
||||||
endTime: string
|
endTime: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type ComplexWithPublicBookingData = Awaited<ReturnType<typeof getComplexWithBookingData>>
|
type ComplexWithPublicBookingData = Awaited<ReturnType<typeof getComplexWithBookingData>>;
|
||||||
|
|
||||||
const DAY_OF_WEEK_BY_INDEX: DayOfWeek[] = [
|
const DAY_OF_WEEK_BY_INDEX: DayOfWeek[] = [
|
||||||
'SUNDAY',
|
'SUNDAY',
|
||||||
@@ -23,44 +23,44 @@ const DAY_OF_WEEK_BY_INDEX: DayOfWeek[] = [
|
|||||||
'THURSDAY',
|
'THURSDAY',
|
||||||
'FRIDAY',
|
'FRIDAY',
|
||||||
'SATURDAY',
|
'SATURDAY',
|
||||||
]
|
];
|
||||||
const BOOKING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
const BOOKING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
const BOOKING_CODE_LENGTH = 6
|
const BOOKING_CODE_LENGTH = 6;
|
||||||
|
|
||||||
export class PublicBookingServiceError extends Error {
|
export class PublicBookingServiceError extends Error {
|
||||||
status: 400 | 403 | 404 | 409
|
status: 400 | 403 | 404 | 409;
|
||||||
|
|
||||||
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
||||||
super(message)
|
super(message);
|
||||||
this.name = 'PublicBookingServiceError'
|
this.name = 'PublicBookingServiceError';
|
||||||
this.status = status
|
this.status = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMinutes(value: string): number {
|
function toMinutes(value: string): number {
|
||||||
const [hours, minutes] = value.split(':').map((part) => Number(part))
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||||
return hours * 60 + minutes
|
return hours * 60 + minutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function minutesToTime(minutes: number): string {
|
function minutesToTime(minutes: number): string {
|
||||||
const safeMinutes = Math.max(0, minutes)
|
const safeMinutes = Math.max(0, minutes);
|
||||||
const hours = Math.floor(safeMinutes / 60)
|
const hours = Math.floor(safeMinutes / 60);
|
||||||
const mins = safeMinutes % 60
|
const mins = safeMinutes % 60;
|
||||||
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`
|
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseIsoDate(date: string): { bookingDate: Date; dayOfWeek: DayOfWeek } {
|
function parseIsoDate(date: string): { bookingDate: Date; dayOfWeek: DayOfWeek } {
|
||||||
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date)
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new PublicBookingServiceError('La fecha debe tener formato YYYY-MM-DD.', 400)
|
throw new PublicBookingServiceError('La fecha debe tener formato YYYY-MM-DD.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const year = Number(match[1])
|
const year = Number(match[1]);
|
||||||
const month = Number(match[2])
|
const month = Number(match[2]);
|
||||||
const day = Number(match[3])
|
const day = Number(match[3]);
|
||||||
|
|
||||||
const bookingDate = new Date(Date.UTC(year, month - 1, day))
|
const bookingDate = new Date(Date.UTC(year, month - 1, day));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Number.isNaN(bookingDate.getTime()) ||
|
Number.isNaN(bookingDate.getTime()) ||
|
||||||
@@ -68,48 +68,48 @@ function parseIsoDate(date: string): { bookingDate: Date; dayOfWeek: DayOfWeek }
|
|||||||
bookingDate.getUTCMonth() + 1 !== month ||
|
bookingDate.getUTCMonth() + 1 !== month ||
|
||||||
bookingDate.getUTCDate() !== day
|
bookingDate.getUTCDate() !== day
|
||||||
) {
|
) {
|
||||||
throw new PublicBookingServiceError('La fecha enviada no es valida.', 400)
|
throw new PublicBookingServiceError('La fecha enviada no es valida.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dayOfWeek = DAY_OF_WEEK_BY_INDEX[bookingDate.getUTCDay()]
|
const dayOfWeek = DAY_OF_WEEK_BY_INDEX[bookingDate.getUTCDay()];
|
||||||
|
|
||||||
if (!dayOfWeek) {
|
if (!dayOfWeek) {
|
||||||
throw new PublicBookingServiceError('No se pudo resolver el dia de la semana.', 400)
|
throw new PublicBookingServiceError('No se pudo resolver el dia de la semana.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { bookingDate, dayOfWeek }
|
return { bookingDate, dayOfWeek };
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatIsoDate(date: Date): string {
|
function formatIsoDate(date: Date): string {
|
||||||
return date.toISOString().slice(0, 10)
|
return date.toISOString().slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateBookingCode(): string {
|
function generateBookingCode(): string {
|
||||||
let code = ''
|
let code = '';
|
||||||
|
|
||||||
for (let index = 0; index < BOOKING_CODE_LENGTH; index += 1) {
|
for (let index = 0; index < BOOKING_CODE_LENGTH; index += 1) {
|
||||||
code += BOOKING_CODE_ALPHABET[randomInt(0, BOOKING_CODE_ALPHABET.length)]
|
code += BOOKING_CODE_ALPHABET[randomInt(0, BOOKING_CODE_ALPHABET.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return code
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasOverlap(slot: Slot, existing: Slot): boolean {
|
function hasOverlap(slot: Slot, existing: Slot): boolean {
|
||||||
return slot.startTime < existing.endTime && slot.endTime > existing.startTime
|
return slot.startTime < existing.endTime && slot.endTime > existing.startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSlots(
|
function buildSlots(
|
||||||
availability: Array<{
|
availability: Array<{
|
||||||
startTime: string
|
startTime: string;
|
||||||
endTime: string
|
endTime: string;
|
||||||
}>,
|
}>,
|
||||||
slotDurationMinutes: number,
|
slotDurationMinutes: number
|
||||||
): Slot[] {
|
): Slot[] {
|
||||||
const slots: Slot[] = []
|
const slots: Slot[] = [];
|
||||||
|
|
||||||
for (const range of availability) {
|
for (const range of availability) {
|
||||||
const start = toMinutes(range.startTime)
|
const start = toMinutes(range.startTime);
|
||||||
const end = toMinutes(range.endTime)
|
const end = toMinutes(range.endTime);
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let current = start;
|
let current = start;
|
||||||
@@ -119,19 +119,19 @@ function buildSlots(
|
|||||||
slots.push({
|
slots.push({
|
||||||
startTime: minutesToTime(current),
|
startTime: minutesToTime(current),
|
||||||
endTime: minutesToTime(current + slotDurationMinutes),
|
endTime: minutesToTime(current + slotDurationMinutes),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slots
|
return slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveSports(data: ComplexWithPublicBookingData) {
|
function resolveSports(data: ComplexWithPublicBookingData) {
|
||||||
const map = new Map<string, { id: string; name: string; slug: string }>()
|
const map = new Map<string, { id: string; name: string; slug: string }>();
|
||||||
|
|
||||||
for (const court of data.courts) {
|
for (const court of data.courts) {
|
||||||
if (!court.sport.isActive) {
|
if (!court.sport.isActive) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!map.has(court.sport.id)) {
|
if (!map.has(court.sport.id)) {
|
||||||
@@ -139,41 +139,41 @@ function resolveSports(data: ComplexWithPublicBookingData) {
|
|||||||
id: court.sport.id,
|
id: court.sport.id,
|
||||||
name: court.sport.name,
|
name: court.sport.name,
|
||||||
slug: court.sport.slug,
|
slug: court.sport.slug,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...map.values()].sort((a, b) => a.name.localeCompare(b.name, 'es'))
|
return [...map.values()].sort((a, b) => a.name.localeCompare(b.name, 'es'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateSportSelection(
|
function validateSportSelection(
|
||||||
availableSports: Array<{ id: string }>,
|
availableSports: Array<{ id: string }>,
|
||||||
sportId: string | undefined,
|
sportId: string | undefined
|
||||||
): { sportSelectionRequired: boolean; selectedSportId: string | undefined } {
|
): { sportSelectionRequired: boolean; selectedSportId: string | undefined } {
|
||||||
const sportSelectionRequired = availableSports.length > 1
|
const sportSelectionRequired = availableSports.length > 1;
|
||||||
|
|
||||||
if (sportSelectionRequired && !sportId) {
|
if (sportSelectionRequired && !sportId) {
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'Debes seleccionar un deporte para consultar disponibilidad.',
|
'Debes seleccionar un deporte para consultar disponibilidad.',
|
||||||
400,
|
400
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sportId && !availableSports.some((sport) => sport.id === sportId)) {
|
if (sportId && !availableSports.some((sport) => sport.id === sportId)) {
|
||||||
throw new PublicBookingServiceError('El deporte seleccionado no pertenece al complejo.', 400)
|
throw new PublicBookingServiceError('El deporte seleccionado no pertenece al complejo.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sportSelectionRequired) {
|
if (sportSelectionRequired) {
|
||||||
return {
|
return {
|
||||||
sportSelectionRequired,
|
sportSelectionRequired,
|
||||||
selectedSportId: sportId,
|
selectedSportId: sportId,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sportSelectionRequired,
|
sportSelectionRequired,
|
||||||
selectedSportId: sportId ?? availableSports[0]?.id,
|
selectedSportId: sportId ?? availableSports[0]?.id,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getComplexWithBookingData(complexSlug: string) {
|
async function getComplexWithBookingData(complexSlug: string) {
|
||||||
@@ -205,48 +205,48 @@ async function getComplexWithBookingData(complexSlug: string) {
|
|||||||
orderBy: { createdAt: 'asc' },
|
orderBy: { createdAt: 'asc' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!complex) {
|
if (!complex) {
|
||||||
throw new PublicBookingServiceError('Complejo no encontrado.', 404)
|
throw new PublicBookingServiceError('Complejo no encontrado.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (complex.plan) {
|
if (complex.plan) {
|
||||||
const rules = parsePlanRules(complex.plan.rules)
|
const rules = parsePlanRules(complex.plan.rules);
|
||||||
|
|
||||||
if (!rules.features.publicBookingPage) {
|
if (!rules.features.publicBookingPage) {
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'El complejo no tiene habilitada la reserva publica.',
|
'El complejo no tiene habilitada la reserva publica.',
|
||||||
403,
|
403
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return complex
|
return complex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapBookingResponse(input: {
|
function mapBookingResponse(input: {
|
||||||
bookingId: string
|
bookingId: string;
|
||||||
bookingCode: string
|
bookingCode: string;
|
||||||
bookingDate: Date
|
bookingDate: Date;
|
||||||
createdAt: Date
|
createdAt: Date;
|
||||||
startTime: string
|
startTime: string;
|
||||||
endTime: string
|
endTime: string;
|
||||||
customerName: string
|
customerName: string;
|
||||||
customerPhone: string
|
customerPhone: string;
|
||||||
status: 'CONFIRMED' | 'CANCELLED'
|
status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED';
|
||||||
court: {
|
court: {
|
||||||
id: string
|
id: string;
|
||||||
name: string
|
name: string;
|
||||||
complexId: string
|
complexId: string;
|
||||||
complexName: string
|
complexName: string;
|
||||||
complexSlug: string
|
complexSlug: string;
|
||||||
sport: {
|
sport: {
|
||||||
id: string
|
id: string;
|
||||||
name: string
|
name: string;
|
||||||
slug: string
|
slug: string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
}) {
|
}) {
|
||||||
return {
|
return {
|
||||||
id: input.bookingId,
|
id: input.bookingId,
|
||||||
@@ -264,11 +264,11 @@ function mapBookingResponse(input: {
|
|||||||
customerPhone: input.customerPhone,
|
customerPhone: input.customerPhone,
|
||||||
status: input.status,
|
status: input.status,
|
||||||
createdAt: input.createdAt.toISOString(),
|
createdAt: input.createdAt.toISOString(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPublicBookingConfirmation(complexSlug: string, bookingCode: string) {
|
export async function getPublicBookingConfirmation(complexSlug: string, bookingCode: string) {
|
||||||
const normalizedBookingCode = bookingCode.toUpperCase()
|
const normalizedBookingCode = bookingCode.toUpperCase();
|
||||||
const booking = await db.courtBooking.findFirst({
|
const booking = await db.courtBooking.findFirst({
|
||||||
where: {
|
where: {
|
||||||
bookingCode: normalizedBookingCode,
|
bookingCode: normalizedBookingCode,
|
||||||
@@ -304,10 +304,10 @@ export async function getPublicBookingConfirmation(complexSlug: string, bookingC
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!booking) {
|
if (!booking) {
|
||||||
throw new PublicBookingServiceError('Reserva no encontrada.', 404)
|
throw new PublicBookingServiceError('Reserva no encontrada.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -325,39 +325,34 @@ export async function getPublicBookingConfirmation(complexSlug: string, bookingC
|
|||||||
},
|
},
|
||||||
status: booking.status,
|
status: booking.status,
|
||||||
createdAt: booking.createdAt.toISOString(),
|
createdAt: booking.createdAt.toISOString(),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listPublicAvailability(
|
export async function listPublicAvailability(complexSlug: string, query: PublicAvailabilityQuery) {
|
||||||
complexSlug: string,
|
const { bookingDate, dayOfWeek } = parseIsoDate(query.date);
|
||||||
query: PublicAvailabilityQuery,
|
const complex = await getComplexWithBookingData(complexSlug);
|
||||||
) {
|
const sports = resolveSports(complex);
|
||||||
const { bookingDate, dayOfWeek } = parseIsoDate(query.date)
|
const sportSelectionRequired = sports.length > 1;
|
||||||
const complex = await getComplexWithBookingData(complexSlug)
|
|
||||||
const sports = resolveSports(complex)
|
|
||||||
const sportSelectionRequired = sports.length > 1
|
|
||||||
|
|
||||||
if (query.sportId && !sports.some((sport) => sport.id === query.sportId)) {
|
if (query.sportId && !sports.some((sport) => sport.id === query.sportId)) {
|
||||||
throw new PublicBookingServiceError('El deporte seleccionado no pertenece al complejo.', 400)
|
throw new PublicBookingServiceError('El deporte seleccionado no pertenece al complejo.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedSportId = sportSelectionRequired
|
const selectedSportId = sportSelectionRequired ? query.sportId : (query.sportId ?? sports[0]?.id);
|
||||||
? query.sportId
|
|
||||||
: (query.sportId ?? sports[0]?.id)
|
|
||||||
|
|
||||||
const candidateCourts = complex.courts.filter((court) => {
|
const candidateCourts = complex.courts.filter((court) => {
|
||||||
if (!court.sport.isActive) {
|
if (!court.sport.isActive) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedSportId) {
|
if (selectedSportId) {
|
||||||
return court.sportId === selectedSportId
|
return court.sportId === selectedSportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
})
|
});
|
||||||
|
|
||||||
const courtIds = candidateCourts.map((court) => court.id)
|
const courtIds = candidateCourts.map((court) => court.id);
|
||||||
|
|
||||||
const bookings =
|
const bookings =
|
||||||
courtIds.length > 0
|
courtIds.length > 0
|
||||||
@@ -375,31 +370,31 @@ export async function listPublicAvailability(
|
|||||||
endTime: true,
|
endTime: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
: []
|
: [];
|
||||||
|
|
||||||
const bookingsByCourt = new Map<string, Slot[]>()
|
const bookingsByCourt = new Map<string, Slot[]>();
|
||||||
|
|
||||||
for (const booking of bookings) {
|
for (const booking of bookings) {
|
||||||
const current = bookingsByCourt.get(booking.courtId) ?? []
|
const current = bookingsByCourt.get(booking.courtId) ?? [];
|
||||||
current.push({
|
current.push({
|
||||||
startTime: booking.startTime,
|
startTime: booking.startTime,
|
||||||
endTime: booking.endTime,
|
endTime: booking.endTime,
|
||||||
})
|
});
|
||||||
bookingsByCourt.set(booking.courtId, current)
|
bookingsByCourt.set(booking.courtId, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
const courts = candidateCourts
|
const courts = candidateCourts
|
||||||
.map((court) => {
|
.map((court) => {
|
||||||
const dayAvailability = court.availabilities.filter(
|
const dayAvailability = court.availabilities.filter(
|
||||||
(availability) => availability.dayOfWeek === dayOfWeek,
|
(availability) => availability.dayOfWeek === dayOfWeek
|
||||||
)
|
);
|
||||||
|
|
||||||
const allSlots = buildSlots(dayAvailability, court.slotDurationMinutes)
|
const allSlots = buildSlots(dayAvailability, court.slotDurationMinutes);
|
||||||
const occupiedSlots = bookingsByCourt.get(court.id) ?? []
|
const occupiedSlots = bookingsByCourt.get(court.id) ?? [];
|
||||||
|
|
||||||
const availableSlots = allSlots.filter(
|
const availableSlots = allSlots.filter(
|
||||||
(slot) => !occupiedSlots.some((occupied) => hasOverlap(slot, occupied)),
|
(slot) => !occupiedSlots.some((occupied) => hasOverlap(slot, occupied))
|
||||||
)
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
courtId: court.id,
|
courtId: court.id,
|
||||||
@@ -412,9 +407,9 @@ export async function listPublicAvailability(
|
|||||||
slotDurationMinutes: court.slotDurationMinutes,
|
slotDurationMinutes: court.slotDurationMinutes,
|
||||||
availabilityDay: dayOfWeek,
|
availabilityDay: dayOfWeek,
|
||||||
availableSlots,
|
availableSlots,
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
.filter((court) => court.availableSlots.length > 0)
|
.filter((court) => court.availableSlots.length > 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
complexId: complex.id,
|
complexId: complex.id,
|
||||||
@@ -424,69 +419,66 @@ export async function listPublicAvailability(
|
|||||||
sportSelectionRequired,
|
sportSelectionRequired,
|
||||||
sports,
|
sports,
|
||||||
courts,
|
courts,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createPublicBooking(
|
export async function createPublicBooking(complexSlug: string, input: CreatePublicBookingInput) {
|
||||||
complexSlug: string,
|
const { bookingDate, dayOfWeek } = parseIsoDate(input.date);
|
||||||
input: CreatePublicBookingInput,
|
const complex = await getComplexWithBookingData(complexSlug);
|
||||||
) {
|
const sports = resolveSports(complex);
|
||||||
const { bookingDate, dayOfWeek } = parseIsoDate(input.date)
|
const { sportSelectionRequired } = validateSportSelection(sports, input.sportId);
|
||||||
const complex = await getComplexWithBookingData(complexSlug)
|
|
||||||
const sports = resolveSports(complex)
|
|
||||||
const { sportSelectionRequired } = validateSportSelection(sports, input.sportId)
|
|
||||||
|
|
||||||
const selectedCourt = complex.courts.find(
|
const selectedCourt = complex.courts.find(
|
||||||
(court) => court.id === input.courtId && court.sport.isActive,
|
(court) => court.id === input.courtId && court.sport.isActive
|
||||||
)
|
);
|
||||||
|
|
||||||
if (!selectedCourt) {
|
if (!selectedCourt) {
|
||||||
throw new PublicBookingServiceError('La cancha seleccionada no existe en el complejo.', 404)
|
throw new PublicBookingServiceError('La cancha seleccionada no existe en el complejo.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sportSelectionRequired && !input.sportId) {
|
if (sportSelectionRequired && !input.sportId) {
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'Debes seleccionar un deporte para reservar en este complejo.',
|
'Debes seleccionar un deporte para reservar en este complejo.',
|
||||||
400,
|
400
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.sportId && input.sportId !== selectedCourt.sportId) {
|
if (input.sportId && input.sportId !== selectedCourt.sportId) {
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'La cancha seleccionada no corresponde al deporte indicado.',
|
'La cancha seleccionada no corresponde al deporte indicado.',
|
||||||
400,
|
400
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dayAvailability = selectedCourt.availabilities.filter(
|
const dayAvailability = selectedCourt.availabilities.filter(
|
||||||
(availability) => availability.dayOfWeek === dayOfWeek,
|
(availability) => availability.dayOfWeek === dayOfWeek
|
||||||
)
|
);
|
||||||
|
|
||||||
const validSlots = buildSlots(dayAvailability, selectedCourt.slotDurationMinutes)
|
const validSlots = buildSlots(dayAvailability, selectedCourt.slotDurationMinutes);
|
||||||
const selectedStartMinutes = toMinutes(input.startTime)
|
const selectedStartMinutes = toMinutes(input.startTime);
|
||||||
const selectedEndMinutes = selectedStartMinutes + selectedCourt.slotDurationMinutes
|
const selectedEndMinutes = selectedStartMinutes + selectedCourt.slotDurationMinutes;
|
||||||
|
|
||||||
const selectedSlot = {
|
const selectedSlot = {
|
||||||
startTime: input.startTime,
|
startTime: input.startTime,
|
||||||
endTime: minutesToTime(selectedEndMinutes),
|
endTime: minutesToTime(selectedEndMinutes),
|
||||||
}
|
};
|
||||||
|
|
||||||
const slotExists = validSlots.some(
|
const slotExists = validSlots.some(
|
||||||
(slot) => slot.startTime === selectedSlot.startTime && slot.endTime === selectedSlot.endTime,
|
(slot) => slot.startTime === selectedSlot.startTime && slot.endTime === selectedSlot.endTime
|
||||||
)
|
);
|
||||||
|
|
||||||
if (!slotExists) {
|
if (!slotExists) {
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'El horario seleccionado no esta disponible para esa cancha.',
|
'El horario seleccionado no esta disponible para esa cancha.',
|
||||||
409,
|
409
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let attempt = 0; attempt < 5; attempt += 1) {
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||||||
try {
|
try {
|
||||||
const booking = await db.$transaction(async (tx) => {
|
const booking = await db.$transaction(async (tx) => {
|
||||||
if (complex.plan) {
|
if (complex.plan) {
|
||||||
const rules = parsePlanRules(complex.plan.rules)
|
const rules = parsePlanRules(complex.plan.rules);
|
||||||
const bookingsForDate = await tx.courtBooking.count({
|
const bookingsForDate = await tx.courtBooking.count({
|
||||||
where: {
|
where: {
|
||||||
bookingDate,
|
bookingDate,
|
||||||
@@ -495,19 +487,19 @@ export async function createPublicBooking(
|
|||||||
complexId: complex.id,
|
complexId: complex.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const violations = evaluatePlanUsage(rules, {
|
const violations = evaluatePlanUsage(rules, {
|
||||||
courtsCount: complex.courts.length,
|
courtsCount: complex.courts.length,
|
||||||
bookingsToday: bookingsForDate,
|
bookingsToday: bookingsForDate,
|
||||||
})
|
});
|
||||||
|
|
||||||
const maxBookingsViolation = violations.find(
|
const maxBookingsViolation = violations.find(
|
||||||
(violation) => violation.code === 'MAX_BOOKINGS_PER_DAY_REACHED',
|
(violation) => violation.code === 'MAX_BOOKINGS_PER_DAY_REACHED'
|
||||||
)
|
);
|
||||||
|
|
||||||
if (maxBookingsViolation) {
|
if (maxBookingsViolation) {
|
||||||
throw new PublicBookingServiceError(maxBookingsViolation.message, 409)
|
throw new PublicBookingServiceError(maxBookingsViolation.message, 409);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,10 +515,10 @@ export async function createPublicBooking(
|
|||||||
gt: selectedSlot.startTime,
|
gt: selectedSlot.startTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
if (overlappingBooking) {
|
if (overlappingBooking) {
|
||||||
throw new PublicBookingServiceError('El horario seleccionado ya fue reservado.', 409)
|
throw new PublicBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.courtBooking.create({
|
return tx.courtBooking.create({
|
||||||
@@ -552,8 +544,8 @@ export async function createPublicBooking(
|
|||||||
customerPhone: true,
|
customerPhone: true,
|
||||||
status: true,
|
status: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
return mapBookingResponse({
|
return mapBookingResponse({
|
||||||
bookingId: booking.id,
|
bookingId: booking.id,
|
||||||
@@ -577,40 +569,40 @@ export async function createPublicBooking(
|
|||||||
slug: selectedCourt.sport.slug,
|
slug: selectedCourt.sport.slug,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof PublicBookingServiceError) {
|
if (error instanceof PublicBookingServiceError) {
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prismaError = error as {
|
const prismaError = error as {
|
||||||
code?: string
|
code?: string;
|
||||||
meta?: {
|
meta?: {
|
||||||
target?: string[] | string
|
target?: string[] | string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
if (prismaError.code === 'P2002') {
|
if (prismaError.code === 'P2002') {
|
||||||
const targets = Array.isArray(prismaError.meta?.target)
|
const targets = Array.isArray(prismaError.meta?.target)
|
||||||
? prismaError.meta?.target
|
? prismaError.meta?.target
|
||||||
: [prismaError.meta?.target]
|
: [prismaError.meta?.target];
|
||||||
const isBookingCodeCollision = targets.some((target) =>
|
const isBookingCodeCollision = targets.some((target) =>
|
||||||
String(target).includes('booking_code'),
|
String(target).includes('booking_code')
|
||||||
)
|
);
|
||||||
|
|
||||||
if (isBookingCodeCollision) {
|
if (isBookingCodeCollision) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new PublicBookingServiceError('El horario seleccionado ya fue reservado.', 409)
|
throw new PublicBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new PublicBookingServiceError(
|
throw new PublicBookingServiceError(
|
||||||
'No se pudo generar un codigo de reserva unico. Intenta nuevamente.',
|
'No se pudo generar un codigo de reserva unico. Intenta nuevamente.',
|
||||||
409,
|
409
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import type { CreateSportInput } from '@repo/api-contract'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { createSport } from '@/modules/sport/services/sport.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { createSport } from '@/modules/sport/services/sport.service'
|
import type { CreateSportInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function createSportHandler(c: AppContext) {
|
export async function createSportHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as CreateSportInput
|
const payload = c.req.valid('json' as never) as CreateSportInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const sport = await createSport(payload)
|
const sport = await createSport(payload);
|
||||||
return c.json(sport, 201)
|
return c.json(sport, 201);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
return c.json({ message: 'No se pudo crear el deporte.' }, 409)
|
return c.json({ message: 'No se pudo crear el deporte.' }, 409);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { listSports } from '@/modules/sport/services/sport.service';
|
||||||
import { listSports } from '@/modules/sport/services/sport.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
export async function listSportsHandler(c: AppContext) {
|
export async function listSportsHandler(c: AppContext) {
|
||||||
const sports = await listSports()
|
const sports = await listSports();
|
||||||
return c.json(sports)
|
return c.json(sports);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import type { UpdateSportInput } from '@repo/api-contract'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { getSportById, updateSport } from '@/modules/sport/services/sport.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { getSportById, updateSport } from '@/modules/sport/services/sport.service'
|
import type { UpdateSportInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type SportIdParams = { id: string }
|
type SportIdParams = { id: string };
|
||||||
|
|
||||||
export async function updateSportHandler(c: AppContext) {
|
export async function updateSportHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as SportIdParams
|
const { id } = c.req.valid('param' as never) as SportIdParams;
|
||||||
const payload = c.req.valid('json' as never) as UpdateSportInput
|
const payload = c.req.valid('json' as never) as UpdateSportInput;
|
||||||
|
|
||||||
const existing = await getSportById(id)
|
const existing = await getSportById(id);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return c.json({ message: 'Deporte no encontrado.' }, 404)
|
return c.json({ message: 'Deporte no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const sport = await updateSport(id, payload)
|
const sport = await updateSport(id, payload);
|
||||||
return c.json(sport)
|
return c.json(sport);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
return c.json({ message: 'No se pudo actualizar el deporte.' }, 409)
|
return c.json({ message: 'No se pudo actualizar el deporte.' }, 409);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { v7 as uuidv7 } from 'uuid'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { db } from '@/lib/prisma';
|
||||||
import { db } from '@/lib/prisma'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
export type CreateSportInput = {
|
export type CreateSportInput = {
|
||||||
name: string
|
name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type UpdateSportInput = {
|
export type UpdateSportInput = {
|
||||||
name?: string
|
name?: string;
|
||||||
isActive?: boolean
|
isActive?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
function slugify(value: string): string {
|
function slugify(value: string): string {
|
||||||
return value
|
return value
|
||||||
@@ -19,18 +19,15 @@ function slugify(value: string): string {
|
|||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
.replace(/-+/g, '-')
|
.replace(/-+/g, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildUniqueSlug(
|
async function buildUniqueSlug(source: string, excludeSportId?: string): Promise<string> {
|
||||||
source: string,
|
const base = slugify(source);
|
||||||
excludeSportId?: string,
|
const fallback = base.length > 0 ? base : `sport-${uuidv7().slice(0, 8)}`;
|
||||||
): Promise<string> {
|
|
||||||
const base = slugify(source)
|
|
||||||
const fallback = base.length > 0 ? base : `sport-${uuidv7().slice(0, 8)}`
|
|
||||||
|
|
||||||
let candidate = fallback
|
let candidate = fallback;
|
||||||
let index = 1
|
let index = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const existing = await db.sport.findFirst({
|
const existing = await db.sport.findFirst({
|
||||||
@@ -45,23 +42,23 @@ async function buildUniqueSlug(
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!existing) return candidate
|
if (!existing) return candidate;
|
||||||
|
|
||||||
index += 1
|
index += 1;
|
||||||
candidate = `${fallback}-${index}`
|
candidate = `${fallback}-${index}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listSports() {
|
export async function listSports() {
|
||||||
return db.sport.findMany({
|
return db.sport.findMany({
|
||||||
orderBy: { name: 'asc' },
|
orderBy: { name: 'asc' },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSport(input: CreateSportInput) {
|
export async function createSport(input: CreateSportInput) {
|
||||||
const slug = await buildUniqueSlug(input.name)
|
const slug = await buildUniqueSlug(input.name);
|
||||||
|
|
||||||
return db.sport.create({
|
return db.sport.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -70,27 +67,27 @@ export async function createSport(input: CreateSportInput) {
|
|||||||
slug,
|
slug,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSportById(id: string) {
|
export async function getSportById(id: string) {
|
||||||
return db.sport.findUnique({ where: { id } })
|
return db.sport.findUnique({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateSport(id: string, input: UpdateSportInput) {
|
export async function updateSport(id: string, input: UpdateSportInput) {
|
||||||
const data: Prisma.SportUncheckedUpdateInput = {}
|
const data: Prisma.SportUncheckedUpdateInput = {};
|
||||||
|
|
||||||
if (input.name) {
|
if (input.name) {
|
||||||
data.name = input.name
|
data.name = input.name;
|
||||||
data.slug = await buildUniqueSlug(input.name, id)
|
data.slug = await buildUniqueSlug(input.name, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.isActive !== undefined) {
|
if (input.isActive !== undefined) {
|
||||||
data.isActive = input.isActive
|
data.isActive = input.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.sport.update({
|
return db.sport.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data,
|
data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,25 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { createSportSchema, updateSportSchema } from '@repo/api-contract'
|
import { requireSuperAdmin } from '@/middlewares/require-super-admin.middleware';
|
||||||
import { Hono } from 'hono'
|
import { createSportHandler } from '@/modules/sport/handlers/create-sport.handler';
|
||||||
import { z } from 'zod'
|
import { listSportsHandler } from '@/modules/sport/handlers/list-sports.handler';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import { updateSportHandler } from '@/modules/sport/handlers/update-sport.handler';
|
||||||
import { requireSuperAdmin } from '@/middlewares/require-super-admin.middleware'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { createSportHandler } from '@/modules/sport/handlers/create-sport.handler'
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import { listSportsHandler } from '@/modules/sport/handlers/list-sports.handler'
|
import { createSportSchema, updateSportSchema } from '@repo/api-contract';
|
||||||
import { updateSportHandler } from '@/modules/sport/handlers/update-sport.handler'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const sportRoutes = new Hono<AppEnv>()
|
export const sportRoutes = new Hono<AppEnv>();
|
||||||
const sportIdParamsSchema = z.object({ id: z.uuid() })
|
const sportIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
|
|
||||||
sportRoutes.use('*', requireAuth)
|
sportRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
sportRoutes.get('/', listSportsHandler)
|
sportRoutes.get('/', listSportsHandler);
|
||||||
sportRoutes.post(
|
sportRoutes.post('/', requireSuperAdmin, zValidator('json', createSportSchema), createSportHandler);
|
||||||
'/',
|
|
||||||
requireSuperAdmin,
|
|
||||||
zValidator('json', createSportSchema),
|
|
||||||
createSportHandler,
|
|
||||||
)
|
|
||||||
sportRoutes.patch(
|
sportRoutes.patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
requireSuperAdmin,
|
requireSuperAdmin,
|
||||||
zValidator('param', sportIdParamsSchema),
|
zValidator('param', sportIdParamsSchema),
|
||||||
zValidator('json', updateSportSchema),
|
zValidator('json', updateSportSchema),
|
||||||
updateSportHandler,
|
updateSportHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { getUserProfile } from '@/modules/user/services/user.service';
|
||||||
import { getUserProfile } from '@/modules/user/services/user.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
export function getUserProfileHandler(c: AppContext) {
|
export function getUserProfileHandler(c: AppContext) {
|
||||||
const authUser = c.get('authUser')
|
const authUser = c.get('authUser');
|
||||||
const profile = getUserProfile(authUser)
|
const profile = getUserProfile(authUser);
|
||||||
return c.json(profile)
|
return c.json(profile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import type { AuthUser } from '@/types/auth-user'
|
import { db } from '@/lib/prisma';
|
||||||
import type { UserProfile } from '@repo/api-contract'
|
import type { UserProfile } from '@repo/api-contract';
|
||||||
|
|
||||||
export function getUserProfile(user: AuthUser): UserProfile {
|
export async function getUserProfile(appUserId: string): Promise<UserProfile | null> {
|
||||||
const fullName =
|
const user = await db.user.findUnique({
|
||||||
(typeof user.user_metadata?.full_name === 'string' &&
|
where: { id: appUserId },
|
||||||
user.user_metadata.full_name) ||
|
select: {
|
||||||
(typeof user.user_metadata?.name === 'string' && user.user_metadata.name) ||
|
id: true,
|
||||||
(user.email ?? 'Usuario')
|
email: true,
|
||||||
|
fullName: true,
|
||||||
|
image: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const role =
|
if (!user) {
|
||||||
(typeof user.app_metadata?.role === 'string' && user.app_metadata.role) ||
|
return null;
|
||||||
'member'
|
}
|
||||||
|
|
||||||
const avatarUrl =
|
|
||||||
(typeof user.user_metadata?.avatar_url === 'string' &&
|
|
||||||
user.user_metadata.avatar_url) ||
|
|
||||||
null
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
fullName,
|
fullName: user.fullName,
|
||||||
email: user.email ?? null,
|
email: user.email,
|
||||||
role,
|
role: 'member',
|
||||||
avatarUrl,
|
avatarUrl: user.image,
|
||||||
createdAt: user.created_at,
|
createdAt: user.createdAt,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Hono } from 'hono'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import { getUserProfileHandler } from '@/modules/user/handlers/get-user-profile.handler';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { getUserProfileHandler } from '@/modules/user/handlers/get-user-profile.handler'
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
export const userRoutes = new Hono<AppEnv>()
|
export const userRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
userRoutes.use('*', requireAuth)
|
userRoutes.use('*', requireAuth);
|
||||||
userRoutes.get('/profile', getUserProfileHandler)
|
userRoutes.get('/profile', getUserProfileHandler);
|
||||||
|
|||||||
@@ -1,23 +1,47 @@
|
|||||||
import { registerApiRoutes } from '@repo/api-contract'
|
import path from 'node:path';
|
||||||
import type { Hono } from 'hono'
|
import { adminBookingRoutes } from '@/modules/admin-booking/admin-booking.routes';
|
||||||
import { complexRoutes } from '@/modules/complex/complex.routes'
|
import { complexRoutes } from '@/modules/complex/complex.routes';
|
||||||
import { courtRoutes } from '@/modules/court/court.routes'
|
import { courtRoutes } from '@/modules/court/court.routes';
|
||||||
import { onboardingRoutes } from '@/modules/onboarding/onboarding.routes'
|
import { onboardingRoutes } from '@/modules/onboarding/onboarding.routes';
|
||||||
import { planRoutes } from '@/modules/plan/plan.routes'
|
import { passwordResetRoutes } from '@/modules/password-reset/password-reset.routes';
|
||||||
import { publicBookingRoutes } from '@/modules/public-booking/public-booking.routes'
|
import { planRoutes } from '@/modules/plan/plan.routes';
|
||||||
import { sportRoutes } from '@/modules/sport/sport.routes'
|
import { publicBookingRoutes } from '@/modules/public-booking/public-booking.routes';
|
||||||
import { userRoutes } from '@/modules/user/user.routes'
|
import { sportRoutes } from '@/modules/sport/sport.routes';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { userRoutes } from '@/modules/user/user.routes';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import type { Hono } from 'hono';
|
||||||
|
import { serveStatic } from 'hono/bun';
|
||||||
|
import { healthCheckRoutes } from './modules/health-check/health-check.routes';
|
||||||
|
|
||||||
export function registerRoutes(app: Hono<AppEnv>) {
|
export function registerRoutes(app: Hono<AppEnv>) {
|
||||||
registerApiRoutes(app, {
|
app
|
||||||
user: userRoutes,
|
.route('/api/user', userRoutes)
|
||||||
plans: planRoutes,
|
.route('/api/plans', planRoutes)
|
||||||
onboarding: onboardingRoutes,
|
.route('/api/onboarding', onboardingRoutes)
|
||||||
complexes: complexRoutes,
|
.route('/api/password-reset', passwordResetRoutes)
|
||||||
sports: sportRoutes,
|
.route('/api/complexes', complexRoutes)
|
||||||
courts: courtRoutes,
|
.route('/api/sports', sportRoutes)
|
||||||
})
|
.route('/api/courts', courtRoutes)
|
||||||
|
.route('/api/public-bookings', publicBookingRoutes)
|
||||||
|
.route('/api/admin-bookings', adminBookingRoutes)
|
||||||
|
.route('/api/health', healthCheckRoutes);
|
||||||
|
|
||||||
app.route('/api/public-bookings', publicBookingRoutes)
|
app.use('*', serveStatic({ root: './public' }));
|
||||||
|
app.get('*', async (c) => {
|
||||||
|
if (c.req.path === '/api' || c.req.path.startsWith('/api/')) {
|
||||||
|
return c.notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep real asset URLs as 404s if the file does not exist.
|
||||||
|
if (path.extname(c.req.path)) {
|
||||||
|
return c.notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexFile = Bun.file('./public/index.html');
|
||||||
|
if (!(await indexFile.exists())) {
|
||||||
|
return c.notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.html(await indexFile.text());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import app from '@/index'
|
import app from '@/index';
|
||||||
import { logger } from '@/lib/logger'
|
import { logger } from '@/lib/logger';
|
||||||
|
|
||||||
const port = Number(Bun.env.PORT ?? 3000)
|
const port = Number(Bun.env.PORT ?? 3000);
|
||||||
const hostname = Bun.env.HOST ?? '0.0.0.0'
|
const hostname = Bun.env.HOST ?? '0.0.0.0';
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
hostname,
|
hostname,
|
||||||
port,
|
port,
|
||||||
fetch: app.fetch,
|
fetch: app.fetch,
|
||||||
})
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info({ url: `http://${server.hostname}:${server.port}` }, 'backend_listening');
|
||||||
{ url: `http://${server.hostname}:${server.port}` },
|
|
||||||
'backend_listening',
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
import type { User } from '@supabase/supabase-js'
|
import type { Session, User } from 'better-auth/types';
|
||||||
|
|
||||||
export type AuthUser = User
|
export type AuthUser = User;
|
||||||
|
export type AuthSession = Session;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import type { Context } from 'hono'
|
import type { AuthUser } from '@/types/auth-user';
|
||||||
import type { AuthUser } from '@/types/auth-user'
|
import type { Context } from 'hono';
|
||||||
|
|
||||||
export type AppVariables = {
|
export type AppVariables = {
|
||||||
authUser: AuthUser
|
authUser: AuthUser;
|
||||||
appUserId: string
|
appUserId: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AppEnv = {
|
export type AppEnv = {
|
||||||
Variables: AppVariables
|
Variables: AppVariables;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type AppContext = Context<AppEnv>
|
export type AppContext = Context<AppEnv>;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
|
||||||
|
|
||||||
FROM oven/bun:1.3 AS deps
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Workspace manifests first for better layer caching.
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
COPY apps/frontend/package.json apps/frontend/package.json
|
|
||||||
COPY packages/api-contract/package.json packages/api-contract/package.json
|
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM deps AS build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY apps/frontend apps/frontend
|
|
||||||
COPY packages/api-contract packages/api-contract
|
|
||||||
|
|
||||||
# Build-time variables for Vite.
|
|
||||||
ARG VITE_API_BASE_URL=http://localhost:3000
|
|
||||||
ARG VITE_SUPABASE_URL=
|
|
||||||
ARG VITE_SUPABASE_ANON_KEY=
|
|
||||||
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
|
||||||
ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL}
|
|
||||||
ENV VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY}
|
|
||||||
|
|
||||||
RUN bun --filter frontend build
|
|
||||||
|
|
||||||
FROM nginx:1.27-alpine AS runner
|
|
||||||
WORKDIR /usr/share/nginx/html
|
|
||||||
|
|
||||||
COPY apps/frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
COPY --from=build /app/apps/frontend/dist ./
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
},
|
},
|
||||||
"iconLibrary": "lucide",
|
"iconLibrary": "lucide",
|
||||||
"rtl": false,
|
"rtl": false,
|
||||||
|
"menuColor": "default",
|
||||||
|
"menuAccent": "subtle",
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "@/components",
|
"components": "@/components",
|
||||||
"utils": "@/lib/utils",
|
"utils": "@/lib/utils",
|
||||||
@@ -19,7 +21,7 @@
|
|||||||
"lib": "@/lib",
|
"lib": "@/lib",
|
||||||
"hooks": "@/hooks"
|
"hooks": "@/hooks"
|
||||||
},
|
},
|
||||||
"menuColor": "default",
|
"registries": {
|
||||||
"menuAccent": "subtle",
|
"@diceui": "https://diceui.com/r/{name}.json"
|
||||||
"registries": {}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import js from '@eslint/js'
|
|
||||||
import globals from 'globals'
|
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
||||||
import tseslint from 'typescript-eslint'
|
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
||||||
|
|
||||||
export default defineConfig([
|
|
||||||
globalIgnores(['dist']),
|
|
||||||
{
|
|
||||||
files: ['**/*.{ts,tsx}'],
|
|
||||||
extends: [
|
|
||||||
js.configs.recommended,
|
|
||||||
tseslint.configs.recommended,
|
|
||||||
reactHooks.configs.flat.recommended,
|
|
||||||
reactRefresh.configs.vite,
|
|
||||||
],
|
|
||||||
languageOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
globals: globals.browser,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>frontend</title>
|
<title>PlayZer</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user