From b3f1b72da448b4f6e1bba3dbf288d85a71694b4f Mon Sep 17 00:00:00 2001 From: Jose Selesan Date: Fri, 17 Apr 2026 08:35:54 -0300 Subject: [PATCH] refactor: migrate authentication from Supabase to Better Auth and update project configuration --- Dockerfile | 6 +- README.md | 183 +++++------------- apps/backend/.env.example | 8 +- apps/backend/prisma/user.prisma | 12 +- apps/backend/src/lib/supabase-admin.ts | 26 --- apps/backend/src/lib/supabase.ts | 18 -- .../onboarding/services/onboarding.service.ts | 73 ------- .../services/password-reset.service.ts | 60 +++--- apps/backend/src/types/auth-user.ts | 3 - apps/frontend/.env.example | 2 - apps/frontend/package.json | 3 +- apps/frontend/vite.config.ts | 3 - bun.lock | 21 -- packages/api-contract/src/onboarding.ts | 1 - 14 files changed, 87 insertions(+), 332 deletions(-) delete mode 100644 apps/backend/src/lib/supabase-admin.ts delete mode 100644 apps/backend/src/lib/supabase.ts delete mode 100644 apps/backend/src/types/auth-user.ts diff --git a/Dockerfile b/Dockerfile index 195a25f..1e58d3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,11 +18,7 @@ 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 # ============================================================================= @@ -70,4 +66,4 @@ COPY entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh EXPOSE 3000 -CMD ["/app/entrypoint.sh"] \ No newline at end of file +CMD ["/app/entrypoint.sh"] diff --git a/README.md b/README.md index 703177e..cad0840 100644 --- a/README.md +++ b/README.md @@ -1,184 +1,105 @@ -# Monorepo (Frontend + Backend + Contrato API) +# Playzer Monorepo Este repositorio contiene: -- `apps/frontend`: app web en React + Vite + TanStack Router/Query. -- `apps/backend`: API en Bun + Hono + Prisma + Supabase Auth. -- `packages/api-contract`: contratos compartidos (schemas/types/rutas) entre frontend y backend. +- `apps/frontend`: React + Vite + TanStack Router/Query. +- `apps/backend`: Bun + Hono + Prisma + Better Auth. +- `packages/api-contract`: contratos Zod/tipos compartidos. -## Objetivo de este README +## Requisitos -Dejarte productivo/a en pocos minutos para que puedas: - -1. Levantar frontend y backend en local. -2. Configurar variables de entorno. -3. Ejecutar migraciones de base de datos. -4. Entender la estructura y el flujo de trabajo del proyecto. - -## Stack técnico - -- Runtime/package manager: `bun` -- Frontend: `React 19`, `Vite`, `TanStack Router`, `TanStack Query` -- Backend: `Bun`, `Hono`, `Prisma`, `PostgreSQL` -- Auth: `Supabase Auth` (token Bearer validado en backend) -- Validación de contratos: `zod` en `@repo/api-contract` - -## Prerrequisitos - -- `bun` instalado (versión reciente). -- `PostgreSQL` corriendo localmente (o accesible remotamente). -- Proyecto de `Supabase` para autenticación (URL + anon key). +- Bun reciente +- PostgreSQL disponible ## Setup rápido -1. Instalar dependencias desde la raíz: +1. Instalar dependencias: ```sh bun install ``` -2. Configurar variables de entorno del backend: +2. Configurar backend: ```sh cp apps/backend/.env.example apps/backend/.env ``` -3. Configurar variables de entorno del frontend: +3. Configurar frontend: ```sh cp apps/frontend/.env.example apps/frontend/.env ``` -4. Revisar/editar valores en ambos `.env` según tu entorno. - -## Variables de entorno - -### Backend (`apps/backend/.env`) - -Variables esperadas: - -- `SUPABASE_URL` -- `SUPABASE_ANON_KEY` -- `CORS_ORIGIN` (por defecto local frontend) -- `LOG_LEVEL` -- `DATABASE_URL` (PostgreSQL) - -Notas: - -- El backend también acepta `VITE_SUPABASE_URL` y `VITE_SUPABASE_ANON_KEY` como fallback. -- Si falta `DATABASE_URL`, Prisma no puede inicializarse. - -### Frontend (`apps/frontend/.env`) - -Variables esperadas: - -- `VITE_SUPABASE_URL` -- `VITE_SUPABASE_ANON_KEY` -- `VITE_API_BASE_URL` (por defecto: `http://localhost:3000`) - -## Base de datos (Prisma) - -Antes de arrancar backend por primera vez, corré migraciones: +4. Ejecutar migraciones: ```sh bun --filter backend prisma:migrate ``` -Si cambiás el schema, regenerá cliente: +## Variables de entorno -```sh -bun --filter backend prisma:generate -``` +### Backend (`apps/backend/.env`) -## Ejecutar en desarrollo +- `DATABASE_URL` +- `BETTER_AUTH_SECRET` +- `BETTER_AUTH_URL` +- `APP_BASE_URL` +- `CORS_ORIGIN` -Terminal 1 (backend): +### Frontend (`apps/frontend/.env`) + +- `VITE_API_BASE_URL` + +## Desarrollo + +Backend: ```sh bun run dev:backend ``` -Terminal 2 (frontend): +Frontend: ```sh bun run dev:frontend ``` -Puertos por defecto: +Todo junto: -- Frontend: `http://localhost:5173` -- Backend: `http://localhost:3000` - -## Scripts útiles - -Desde la raíz: - -- `bun run dev:frontend` -- `bun run dev:backend` -- `bun run build:frontend` -- `bun run lint:frontend` - -Desde `apps/backend`: - -- `bun run dev` -- `bun run start` -- `bun run prisma:migrate` -- `bun run prisma:generate` - -## Estructura del repo - -```txt -. -├─ apps/ -│ ├─ frontend/ # UI y navegación -│ └─ backend/ # API, auth middleware, acceso a DB -└─ packages/ - └─ api-contract/ # Schemas/tipos/rutas compartidos +```sh +bun run dev ``` -## Flujo de autenticación (resumen) +## Prisma -1. Frontend autentica con Supabase. -2. Frontend guarda el access token y lo envía como `Authorization: Bearer ...` al backend. -3. Backend valida token con Supabase (`requireAuth` middleware). -4. Si el token es válido, habilita rutas protegidas (`/api/user/*`, `/api/tenants/*`). +Regenerar cliente después de cambios de schema: -## Endpoints principales +```sh +bun --filter backend prisma:generate +``` -- `GET /api/user/profile` -- `POST /api/tenants` -- `GET /api/tenants/:id` -- `PATCH /api/tenants/:id` +## Autenticación -## Convenciones recomendadas para el equipo +El proyecto usa **Better Auth** con sesión por cookie. -- Mantener contratos de API en `packages/api-contract` (evitar duplicar tipos). -- Si agregás/cambiás endpoint: - 1. Actualizá schema/tipos en `api-contract`. - 2. Implementá handler/ruta en backend. - 3. Consumí el contrato desde frontend. -- Validar payloads con Zod tanto en contrato como en rutas. -- Mantener PRs chicos y enfocados. +- Backend: `c.get('user')` y `c.get('session')` en contexto Hono. +- Frontend: `authClient` con `fetchOptions.credentials = 'include'`. +- Frontend API: Axios con `withCredentials = true`. +- Backend CORS: `credentials: true`. -## Troubleshooting rápido +## Deploy (Docker) -- Error de CORS: - - Revisar `CORS_ORIGIN` en backend y que incluya `http://localhost:5173`. -- `403 Missing bearer token`: - - Verificar login en frontend y envío del header `Authorization`. -- Error de Supabase env vars: - - Confirmar `SUPABASE_URL`/`SUPABASE_ANON_KEY` (backend) y `VITE_*` (frontend). -- Error de Prisma/DB: - - Confirmar `DATABASE_URL`, conectividad a PostgreSQL y migraciones aplicadas. +- Build context: raíz del monorepo (`./`) +- Dockerfile: `./Dockerfile` +- Build args requeridos: + - `DATABASE_URL` + - `VITE_API_BASE_URL` + - `BETTER_AUTH_SECRET` + - `BETTER_AUTH_URL` -## Primer día sugerido para onboarding +## Flujo recomendado de cambios de API -1. Levantar backend y frontend en local. -2. Crear usuario o iniciar sesión desde la pantalla de onboard. -3. Crear un tenant de prueba. -4. Probar lectura/edición de tenant. -5. Revisar `packages/api-contract` para entender cómo se comparten tipos y rutas. - ---- - -Si te trabás en el setup, empezá verificando `.env`, estado de PostgreSQL y que ambos procesos (`dev:backend` y `dev:frontend`) estén corriendo. +1. Actualizar contrato en `packages/api-contract`. +2. Implementar backend en `apps/backend`. +3. Consumir contrato desde frontend (`@repo/api-contract`). diff --git a/apps/backend/.env.example b/apps/backend/.env.example index cccf350..31461ae 100644 --- a/apps/backend/.env.example +++ b/apps/backend/.env.example @@ -1,9 +1,5 @@ -SUPABASE_URL=https://your-project-ref.supabase.co -SUPABASE_ANON_KEY=your-anon-key -SUPABASE_SERVICE_ROLE_KEY=your-service-role-key -# Fallbacks accepted too: -# VITE_SUPABASE_URL=https://your-project-ref.supabase.co -# VITE_SUPABASE_ANON_KEY=your-anon-key +BETTER_AUTH_SECRET=your-random-secret +BETTER_AUTH_URL=http://localhost:3000 CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173 LOG_LEVEL=debug DATABASE_URL=postgresql://postgres:postgres@localhost:5432/monorepo diff --git a/apps/backend/prisma/user.prisma b/apps/backend/prisma/user.prisma index 2206dfa..354d44d 100644 --- a/apps/backend/prisma/user.prisma +++ b/apps/backend/prisma/user.prisma @@ -1,11 +1 @@ -// 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") -// } +// User model is defined in auth.prisma (Better Auth schema). diff --git a/apps/backend/src/lib/supabase-admin.ts b/apps/backend/src/lib/supabase-admin.ts deleted file mode 100644 index f46eb4a..0000000 --- a/apps/backend/src/lib/supabase-admin.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createClient } from '@supabase/supabase-js'; - -let cachedClient: ReturnType | 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; -} diff --git a/apps/backend/src/lib/supabase.ts b/apps/backend/src/lib/supabase.ts deleted file mode 100644 index 7c8e13e..0000000 --- a/apps/backend/src/lib/supabase.ts +++ /dev/null @@ -1,18 +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, - }, -}); diff --git a/apps/backend/src/modules/onboarding/services/onboarding.service.ts b/apps/backend/src/modules/onboarding/services/onboarding.service.ts index f709132..d28410d 100644 --- a/apps/backend/src/modules/onboarding/services/onboarding.service.ts +++ b/apps/backend/src/modules/onboarding/services/onboarding.service.ts @@ -2,14 +2,12 @@ import { createHash, randomInt } from 'node:crypto'; import { auth } from '@/lib/auth'; import { sendMail } from '@/lib/mailer'; import { db } from '@/lib/prisma'; -import { getSupabaseAdminClient } from '@/lib/supabase-admin'; import type { OnboardingCompleteInput, OnboardingResendOtpInput, OnboardingStartInput, OnboardingVerifyOtpInput, } from '@repo/api-contract'; -import type { User as SupabaseAuthUser } from '@supabase/supabase-js'; import { v7 as uuidv7 } from 'uuid'; const OTP_LENGTH = 6; @@ -126,77 +124,6 @@ async function sendOtpEmail(email: string, otpCode: string) { }); } -async function findSupabaseUserByEmail(email: string): Promise { - let page = 1; - const perPage = 100; - - 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 { - 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 { const email = normalizeEmail(input.email); const otpCode = createOtpCode(); diff --git a/apps/backend/src/modules/password-reset/services/password-reset.service.ts b/apps/backend/src/modules/password-reset/services/password-reset.service.ts index be62314..9ed1c41 100644 --- a/apps/backend/src/modules/password-reset/services/password-reset.service.ts +++ b/apps/backend/src/modules/password-reset/services/password-reset.service.ts @@ -1,7 +1,7 @@ import { createHash, randomInt } from 'node:crypto'; +import { auth } from '@/lib/auth'; import { sendMail } from '@/lib/mailer'; import { db } from '@/lib/prisma'; -import { getSupabaseAdminClient } from '@/lib/supabase-admin'; import type { PasswordResetResendInput, PasswordResetStartInput, @@ -189,34 +189,17 @@ async function sendPasswordChangedEmail( }); } -async function findSupabaseUserByEmail(email: string): Promise<{ id: string } | null> { - let page = 1; - const perPage = 100; +async function findBetterAuthUserByEmail(email: string): Promise<{ id: string } | null> { + const user = await db.user.findUnique({ + where: { email }, + select: { id: true }, + }); - 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 { id: found.id }; - } - - if (data.users.length < perPage) { - return null; - } - - page += 1; + if (!user) { + return null; } - return null; + return { id: user.id }; } export async function startPasswordReset( @@ -224,7 +207,7 @@ export async function startPasswordReset( ): Promise { const email = normalizeEmail(input.email); - const existingUser = await findSupabaseUserByEmail(email); + const existingUser = await findBetterAuthUserByEmail(email); if (!existingUser) { return { message: 'Si el email existe, recibirás un código de verificación.', @@ -369,13 +352,30 @@ export async function verifyOtpAndResetPassword( throw new PasswordResetError('Código incorrecto.', 400); } - const existingUser = await findSupabaseUserByEmail(request.email); + const existingUser = await findBetterAuthUserByEmail(request.email); if (!existingUser) { throw new PasswordResetError('Usuario no encontrado.', 404); } - await getSupabaseAdminClient().auth.admin.updateUserById(existingUser.id, { - password: newPassword, + const resetToken = uuidv7(); + const identifier = `reset-password:${resetToken}`; + const expiresAt = nowPlusMinutes(OTP_TTL_MINUTES); + + await db.verification.create({ + data: { + id: uuidv7(), + identifier, + value: existingUser.id, + expiresAt, + }, + }); + + await auth.api.resetPassword({ + asResponse: false, + body: { + token: resetToken, + newPassword, + }, }); await db.passwordResetRequest.update({ diff --git a/apps/backend/src/types/auth-user.ts b/apps/backend/src/types/auth-user.ts deleted file mode 100644 index edd5e59..0000000 --- a/apps/backend/src/types/auth-user.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { User } from '@supabase/supabase-js'; - -export type AuthUser = User; diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example index 86a9cf4..99cbcd5 100644 --- a/apps/frontend/.env.example +++ b/apps/frontend/.env.example @@ -1,3 +1 @@ -VITE_SUPABASE_URL=https://your-project-ref.supabase.co -VITE_SUPABASE_ANON_KEY=your-anon-key VITE_API_BASE_URL=http://localhost:3000 diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 3afe3e4..78f8456 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -18,7 +18,6 @@ "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-slot": "^1.2.4", "@repo/api-contract": "workspace:*", - "@supabase/supabase-js": "^2.101.1", "@tanstack/react-query": "^5.96.1", "@tanstack/react-query-devtools": "^5.96.1", "@tanstack/react-router": "^1.168.10", @@ -52,4 +51,4 @@ "typescript": "~5.9.3", "vite": "^8.0.1" } -} \ No newline at end of file +} diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts index 8a2cda7..675cdb3 100644 --- a/apps/frontend/vite.config.ts +++ b/apps/frontend/vite.config.ts @@ -22,9 +22,6 @@ export default defineConfig({ if (id.includes('node_modules/@tanstack/react-router')) { return 'router-vendor'; } - if (id.includes('node_modules/@supabase/supabase-js')) { - return 'supabase-vendor'; - } if (id.includes('node_modules/radix-ui') || id.includes('node_modules/lucide-react')) { return 'ui-vendor'; } diff --git a/bun.lock b/bun.lock index 2b6a44e..0f20b6b 100644 --- a/bun.lock +++ b/bun.lock @@ -47,7 +47,6 @@ "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-slot": "^1.2.4", "@repo/api-contract": "workspace:*", - "@supabase/supabase-js": "^2.101.1", "@tanstack/react-query": "^5.96.1", "@tanstack/react-query-devtools": "^5.96.1", "@tanstack/react-router": "^1.168.10", @@ -515,20 +514,6 @@ "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], - "@supabase/auth-js": ["@supabase/auth-js@2.101.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-Kd0Wey+RkFHgyVep7adS6UOE2pN6MJ3mZ32PAXSvfw6IjUkFRC7IQpdZZjUOcUe5pXr1ejufCRgF6lsGINe4Tw=="], - - "@supabase/functions-js": ["@supabase/functions-js@2.101.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-OZWU7YtaG+NNNFZK8p/FuJ6gpq7pFyrG2fLOopP73HAIDHDGpOttPJapvO8ADu3RkqfQfkwrB354vPkSBbZ20A=="], - - "@supabase/phoenix": ["@supabase/phoenix@0.4.0", "", {}, "sha512-RHSx8bHS02xwfHdAbX5Lpbo6PXbgyf7lTaXTlwtFDPwOIw64NnVRwFAXGojHhjtVYI+PEPNSWwkL90f4agN3bw=="], - - "@supabase/postgrest-js": ["@supabase/postgrest-js@2.101.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-UW1RajH5jbZoK+ldAJ1I6VZ+HWwZ2oaKjEQ6Gn+AQ67CHQVxGl8wNQoLYyumbyaExm41I+wn7arulcY1eHeZJw=="], - - "@supabase/realtime-js": ["@supabase/realtime-js@2.101.1", "", { "dependencies": { "@supabase/phoenix": "^0.4.0", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-Oa6dno0OB9I+hv5do5zsZHbFu41ViZnE9IWjmkeeF/8fPmB5fWoHGqeTYEC3/0DAgtpUoFJa4FpvzFH0SBHo1Q=="], - - "@supabase/storage-js": ["@supabase/storage-js@2.101.1", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-WhTaUOBgeEvnKLy95Cdlp6+D5igSF/65yC727w1olxbet5nzUvMlajKUWyzNtQu2efrz2cQ7FcdVBdQqgT9YKQ=="], - - "@supabase/supabase-js": ["@supabase/supabase-js@2.101.1", "", { "dependencies": { "@supabase/auth-js": "2.101.1", "@supabase/functions-js": "2.101.1", "@supabase/postgrest-js": "2.101.1", "@supabase/realtime-js": "2.101.1", "@supabase/storage-js": "2.101.1" } }, "sha512-Jnhm3LfuACwjIzvk2pfUbGQn7pa7hi6MFzfSyPrRYWVCCu69RPLCFyHSBl7HSBwadbQ3UZOznnD3gPca3ePrRA=="], - "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], @@ -611,8 +596,6 @@ "@types/validate-npm-package-name": ["@types/validate-npm-package-name@4.0.2", "", {}, "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw=="], - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -945,8 +928,6 @@ "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], - "iceberg-js": ["iceberg-js@0.8.1", "", {}, "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA=="], - "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -1461,8 +1442,6 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], - "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], diff --git a/packages/api-contract/src/onboarding.ts b/packages/api-contract/src/onboarding.ts index 8a8861e..628bc37 100644 --- a/packages/api-contract/src/onboarding.ts +++ b/packages/api-contract/src/onboarding.ts @@ -93,7 +93,6 @@ export const onboardingCompleteResponseSchema = z.object({ userId: z.uuid(), complexId: z.uuid(), complexSlug: z.string(), - supabaseUserId: z.uuid(), }) export type OnboardingStartInput = z.infer