Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/schema-graph.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
type Recipe {
id: ID!
title: String!
description: String!
prepTimeMinutes: Int!
servings: Int!
category: String!
imageUrl: String
tags: [Tag!]!
ingredients: [RecipeIngredient!]!
steps: [Step!]!
author: User!
averageRating: Float
}

type Query {
recipes(search: String, tags: [String!], category: String, sortBy: RecipeSortField, page: Int, pageSize: Int): RecipeConnection!
recipe(id: ID!): Recipe
me: User
}

type RecipeConnection {
items: [Recipe!]!
totalCount: Int!
}

enum RecipeSortField {
NEWEST
RATING
PREP_TIME
}
5 changes: 5 additions & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
# Keep environment variables out of version control
.env

/src/generated/prisma
19 changes: 12 additions & 7 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,32 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "vitest"
},
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "vitest"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"prisma": { "seed": "tsx prisma/seed.ts" },
"devDependencies": {
"@types/cookie-parser": "^1.4.10",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/multer": "^2.2.0",
"@types/node": "^24.19.0",
"@types/supertest": "^7.2.1",
"prisma": "^8.0.0-rc.17",
"dotenv": "^16.6.1",
"prisma": "^6.19.3",
"supertest": "^7.3.0",
"tsx": "^4.23.15",
"typescript": "^6.0.3",
"vitest": "^5.0.2"
},
"dependencies": {
"@prisma/client": "^6.19.3"
}
}
189 changes: 189 additions & 0 deletions server/prisma/migrations/20260930094150_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
-- CreateEnum
CREATE TYPE "RecipeStatus" AS ENUM ('DRAFT', 'PUBLISHED');

-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"googleId" TEXT,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"languagePref" TEXT NOT NULL DEFAULT 'nb',
"unitSystem" TEXT NOT NULL DEFAULT 'metric',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Recipe" (
"id" TEXT NOT NULL,
"authorId" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"prepTimeMinutes" INTEGER NOT NULL,
"servings" INTEGER NOT NULL,
"category" TEXT NOT NULL,
"imageUrl" TEXT,
"status" "RecipeStatus" NOT NULL DEFAULT 'DRAFT',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Recipe_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "RecipeIngredient" (
"id" TEXT NOT NULL,
"recipeId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"amount" DOUBLE PRECISION NOT NULL,
"unit" TEXT NOT NULL,
"order" INTEGER NOT NULL,

CONSTRAINT "RecipeIngredient_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Step" (
"id" TEXT NOT NULL,
"recipeId" TEXT NOT NULL,
"order" INTEGER NOT NULL,
"instruction" TEXT NOT NULL,

CONSTRAINT "Step_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Tag" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,

CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Allergen" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,

CONSTRAINT "Allergen_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Favorite" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"recipeId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Favorite_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Rating" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"recipeId" TEXT NOT NULL,
"score" INTEGER NOT NULL,
"comment" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Rating_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "SearchHistory" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"query" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "SearchHistory_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "_RecipeTags" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,

CONSTRAINT "_RecipeTags_AB_pkey" PRIMARY KEY ("A","B")
);

-- CreateTable
CREATE TABLE "_UserAllergens" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,

CONSTRAINT "_UserAllergens_AB_pkey" PRIMARY KEY ("A","B")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_googleId_key" ON "User"("googleId");

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- CreateIndex
CREATE INDEX "Recipe_category_idx" ON "Recipe"("category");

-- CreateIndex
CREATE INDEX "Recipe_createdAt_idx" ON "Recipe"("createdAt");

-- CreateIndex
CREATE INDEX "RecipeIngredient_recipeId_idx" ON "RecipeIngredient"("recipeId");

-- CreateIndex
CREATE INDEX "Step_recipeId_idx" ON "Step"("recipeId");

-- CreateIndex
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");

-- CreateIndex
CREATE UNIQUE INDEX "Allergen_name_key" ON "Allergen"("name");

-- CreateIndex
CREATE UNIQUE INDEX "Favorite_userId_recipeId_key" ON "Favorite"("userId", "recipeId");

-- CreateIndex
CREATE UNIQUE INDEX "Rating_userId_recipeId_key" ON "Rating"("userId", "recipeId");

-- CreateIndex
CREATE INDEX "_RecipeTags_B_index" ON "_RecipeTags"("B");

-- CreateIndex
CREATE INDEX "_UserAllergens_B_index" ON "_UserAllergens"("B");

-- AddForeignKey
ALTER TABLE "Recipe" ADD CONSTRAINT "Recipe_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "RecipeIngredient" ADD CONSTRAINT "RecipeIngredient_recipeId_fkey" FOREIGN KEY ("recipeId") REFERENCES "Recipe"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Step" ADD CONSTRAINT "Step_recipeId_fkey" FOREIGN KEY ("recipeId") REFERENCES "Recipe"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_recipeId_fkey" FOREIGN KEY ("recipeId") REFERENCES "Recipe"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Rating" ADD CONSTRAINT "Rating_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Rating" ADD CONSTRAINT "Rating_recipeId_fkey" FOREIGN KEY ("recipeId") REFERENCES "Recipe"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "SearchHistory" ADD CONSTRAINT "SearchHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_RecipeTags" ADD CONSTRAINT "_RecipeTags_A_fkey" FOREIGN KEY ("A") REFERENCES "Recipe"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_RecipeTags" ADD CONSTRAINT "_RecipeTags_B_fkey" FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_UserAllergens" ADD CONSTRAINT "_UserAllergens_A_fkey" FOREIGN KEY ("A") REFERENCES "Allergen"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_UserAllergens" ADD CONSTRAINT "_UserAllergens_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions server/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
120 changes: 120 additions & 0 deletions server/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//Oversikt over strukturen på backend, inneholder ikke handleliste enda med ellers alt av grunnleggende struktur
//bruk av @@index er bevist for å kunne filtrere på tidspunkt created, og kategori
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
//brukeren sin oversikt
model User {
id String @id @default(uuid())
googleId String? @unique
name String
email String @unique
languagePref String @default("nb")
unitSystem String @default("metric")
createdAt DateTime @default(now())
recipes Recipe[]
favorites Favorite[]
ratings Rating[]
searches SearchHistory[]
allergens Allergen[] @relation("UserAllergens")
}
//oppskriftoversikt
model Recipe {
id String @id @default(uuid())
author User @relation(fields: [authorId], references: [id])
authorId String
title String
description String
prepTimeMinutes Int
servings Int
category String
imageUrl String?
status RecipeStatus @default(DRAFT)
createdAt DateTime @default(now())
ingredients RecipeIngredient[]
steps Step[]
tags Tag[] @relation("RecipeTags")
favorites Favorite[]
ratings Rating[]
@@index([category])
@@index([createdAt])
}

enum RecipeStatus {
DRAFT
PUBLISHED
}

model RecipeIngredient {
id String @id @default(uuid())
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
recipeId String
name String
amount Float
unit String
order Int
@@index([recipeId])
}

model Step {
id String @id @default(uuid())
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
recipeId String
order Int
instruction String
@@index([recipeId])
}

model Tag {
id String @id @default(uuid())
name String @unique
recipes Recipe[] @relation("RecipeTags")
}

model Allergen {
id String @id @default(uuid())
name String @unique
users User[] @relation("UserAllergens")
}

model Favorite {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
userId String
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
recipeId String
createdAt DateTime @default(now())
@@unique([userId, recipeId])
}

model Rating {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
userId String
recipe Recipe @relation(fields: [recipeId], references: [id], onDelete: Cascade)
recipeId String
score Int
comment String?
createdAt DateTime @default(now())
@@unique([userId, recipeId])
}
//tenkte dette var fint for hukommelse senere
model SearchHistory {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
userId String
query String
createdAt DateTime @default(now())
}
Loading