diff --git a/.gitignore b/.gitignore index cb2b88a..169a5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ Thumbs.db # Misc .cache/ + +# Claude Code per-user settings +.claude/settings.local.json diff --git a/package.json b/package.json index 5293a18..c009aa6 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,12 @@ "typecheck": "tsc -b", "test": "pnpm --filter \"./packages/*\" --filter \"./apps/*\" -r --if-present test", "test:e2e": "pnpm --filter @ketopath/web test:e2e", + "db:generate": "pnpm --filter @ketopath/db db:generate", + "db:migrate": "pnpm --filter @ketopath/db db:migrate", + "db:migrate:deploy": "pnpm --filter @ketopath/db db:migrate:deploy", + "db:studio": "pnpm --filter @ketopath/db db:studio", + "db:format": "pnpm --filter @ketopath/db db:format", + "db:seed": "pnpm --filter @ketopath/db db:seed", "prepare": "husky" }, "devDependencies": { diff --git a/packages/db/.env.example b/packages/db/.env.example new file mode 100644 index 0000000..b9e34d3 --- /dev/null +++ b/packages/db/.env.example @@ -0,0 +1 @@ +DATABASE_URL="postgresql://luciano:luciano%40@localhost:5432/ketopath?schema=public" diff --git a/packages/db/.gitignore b/packages/db/.gitignore new file mode 100644 index 0000000..f8028b9 --- /dev/null +++ b/packages/db/.gitignore @@ -0,0 +1,2 @@ +.env +*.tsbuildinfo diff --git a/packages/db/package.json b/packages/db/package.json index ebdd467..6a34cf6 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -10,10 +10,25 @@ }, "scripts": { "typecheck": "tsc --noEmit", - "lint": "eslint src --max-warnings=0" + "lint": "eslint src --max-warnings=0", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate dev", + "db:migrate:deploy": "prisma migrate deploy", + "db:studio": "prisma studio", + "db:format": "prisma format", + "db:seed": "tsx prisma/seed.ts" + }, + "dependencies": { + "@prisma/client": "^5.18.0" }, "devDependencies": { "@ketopath/eslint-config": "workspace:*", - "@ketopath/tsconfig": "workspace:*" + "@ketopath/tsconfig": "workspace:*", + "prisma": "^5.18.0", + "tsx": "^4.16.2" + }, + "prisma": { + "schema": "prisma/schema.prisma", + "seed": "tsx prisma/seed.ts" } } diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma new file mode 100644 index 0000000..b0e94e9 --- /dev/null +++ b/packages/db/prisma/schema.prisma @@ -0,0 +1,101 @@ +// KetoPath — schema Prisma +// Schema iniziale: solo le 3 entità di base (User, Profile, Preferences). +// Le altre entità del PRD §8 (MealPlan, Recipe, WeightEntry, ecc.) verranno +// aggiunte in step successivi, allineate alle feature corrispondenti. + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +enum Role { + USER + COACH +} + +enum Gender { + MALE + FEMALE + OTHER +} + +enum ActivityLevel { + SEDENTARY + LIGHT + MODERATE + INTENSE +} + +enum Phase { + INTENSIVE + TRANSITION + MAINTENANCE +} + +enum CookingTime { + LOW + MEDIUM + HIGH +} + +enum FastingProtocol { + FOURTEEN_TEN + SIXTEEN_EIGHT + EIGHTEEN_SIX + TWENTY_FOUR + ESE_24 + FIVE_TWO +} + +model User { + id String @id @default(cuid()) + email String @unique + role Role @default(USER) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + profile Profile? + preferences Preferences? + + @@map("users") +} + +model Profile { + id String @id @default(cuid()) + userId String @unique @map("user_id") + age Int + gender Gender + heightCm Int @map("height_cm") + weightStartKg Decimal @map("weight_start_kg") @db.Decimal(5, 2) + weightCurrentKg Decimal @map("weight_current_kg") @db.Decimal(5, 2) + weightGoalKg Decimal @map("weight_goal_kg") @db.Decimal(5, 2) + activityLevel ActivityLevel @map("activity_level") + targetDate DateTime? @map("target_date") @db.Date + currentPhase Phase @default(INTENSIVE) @map("current_phase") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("profiles") +} + +model Preferences { + id String @id @default(cuid()) + userId String @unique @map("user_id") + exclusions String[] @default([]) + cuisinePreferences String[] @default([]) @map("cuisine_preferences") + cookingTime CookingTime @default(MEDIUM) @map("cooking_time") + fastingProtocol FastingProtocol? @map("fasting_protocol") + notificationSettings Json @default("{}") @map("notification_settings") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("preferences") +} diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts new file mode 100644 index 0000000..46e0e8d --- /dev/null +++ b/packages/db/prisma/seed.ts @@ -0,0 +1,20 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main(): Promise { + // TODO: dati di seed (utenti demo, ricette base, ecc.) — verranno aggiunti + // quando le entità relative saranno modellate. + // eslint-disable-next-line no-console + console.info('[seed] nothing to seed yet'); +} + +main() + .catch((err: unknown) => { + // eslint-disable-next-line no-console + console.error(err); + process.exitCode = 1; + }) + .finally(() => { + void prisma.$disconnect(); + }); diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts new file mode 100644 index 0000000..655e39f --- /dev/null +++ b/packages/db/src/client.ts @@ -0,0 +1,13 @@ +import { PrismaClient } from '@prisma/client'; + +const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; + +export const prisma: PrismaClient = + globalForPrisma.prisma ?? + new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['warn', 'error'] : ['error'], + }); + +if (process.env.NODE_ENV !== 'production') { + globalForPrisma.prisma = prisma; +} diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index cb0ff5c..e733bd1 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -1 +1,11 @@ -export {}; +export { prisma } from './client.js'; +export { + ActivityLevel, + CookingTime, + FastingProtocol, + Gender, + Phase, + Prisma, + Role, +} from '@prisma/client'; +export type { Preferences, Profile, User } from '@prisma/client'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ce3076..acb7b3d 100644 Binary files a/pnpm-lock.yaml and b/pnpm-lock.yaml differ