diff --git a/backend/Dockerfile b/backend/Dockerfile index 5414fca8..dd608ea7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,33 +1,33 @@ # Byggas från backend-mappen: docker build -t recipe-api:local . -# Stage 1: Bygg applikationen +# Stage 1: Installera enbart produktionsberoenden (exkl. devDeps) +FROM node:24.15.0-alpine AS deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev +RUN ./node_modules/.bin/prisma generate + +# Stage 2: Bygg applikationen (alla beroenden, kör tester) FROM node:24.15.0-alpine AS builder WORKDIR /app - -# Kopiera backend-filer -COPY package.json ./ -COPY package-lock.json ./ +COPY package.json package-lock.json ./ +RUN npm ci COPY prisma ./prisma COPY src ./src -COPY tsconfig.json ./ -COPY nest-cli.json ./ - -# Köra npm ci för reproducerbara builds -RUN npm ci - -RUN npx prisma generate +COPY tsconfig.json nest-cli.json ./ +RUN ./node_modules/.bin/prisma generate RUN npm test RUN npm run build -# Stage 2: Kör applikationen +# Stage 3: Kör applikationen FROM node:24.15.0-alpine AS runner WORKDIR /app ENV NODE_ENV=production -COPY --from=builder /app/package.json ./package.json -COPY --from=builder /app/node_modules ./node_modules +COPY package.json ./ +COPY --from=deps /app/node_modules ./node_modules COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/dist ./dist EXPOSE 8080 -CMD ["sh", "-c", "until npx prisma migrate deploy; do echo 'Migration failed, retrying in 5s...'; sleep 5; done && node dist/main"] +CMD ["sh", "-c", "until ./node_modules/.bin/prisma migrate deploy; do echo 'Migration failed, retrying in 5s...'; sleep 5; done && node dist/main"] diff --git a/backend/package-lock.json b/backend/package-lock.json index 795e2532..9baa15f5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -22,6 +22,7 @@ "multer": "^1.4.5-lts.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", + "prisma": "6.12.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "sharp": "^0.33.5", @@ -39,7 +40,6 @@ "@types/passport-jwt": "^4.0.1", "@types/uuid": "^10.0.0", "jest": "^29.7.0", - "prisma": "6.12.0", "ts-jest": "^29.2.6", "typescript": "^5.4.5" } @@ -2088,7 +2088,6 @@ "version": "6.12.0", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.12.0.tgz", "integrity": "sha512-HovZWzhWEMedHxmjefQBRZa40P81N7/+74khKFz9e1AFjakcIQdXgMWKgt20HaACzY+d1LRBC+L4tiz71t9fkg==", - "devOptional": true, "license": "Apache-2.0", "dependencies": { "jiti": "2.4.2" @@ -2098,14 +2097,12 @@ "version": "6.12.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.12.0.tgz", "integrity": "sha512-plbz6z72orcqr0eeio7zgUrZj5EudZUpAeWkFTA/DDdXEj28YHDXuiakvR6S7sD6tZi+jiwQEJAPeV6J6m/tEQ==", - "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.12.0.tgz", "integrity": "sha512-4BRZZUaAuB4p0XhTauxelvFs7IllhPmNLvmla0bO1nkECs8n/o1pUvAVbQ/VOrZR5DnF4HED0PrGai+rIOVePA==", - "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -2119,14 +2116,12 @@ "version": "6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc.tgz", "integrity": "sha512-70vhecxBJlRr06VfahDzk9ow4k1HIaSfVUT3X0/kZoHCMl9zbabut4gEXAyzJZxaCGi5igAA7SyyfBI//mmkbQ==", - "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.12.0.tgz", "integrity": "sha512-EamoiwrK46rpWaEbLX9aqKDPOd8IyLnZAkiYXFNuq0YsU0Z8K09/rH8S7feOWAVJ3xzeSgcEJtBlVDrajM9Sag==", - "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.12.0", @@ -2138,7 +2133,6 @@ "version": "6.12.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.12.0.tgz", "integrity": "sha512-nRerTGhTlgyvcBlyWgt8OLNIV7QgJS2XYXMJD1hysorMCuLAjuDDuoxmVt7C2nLxbuxbWPp7OuFRHC23HqD9dA==", - "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.12.0" @@ -5983,7 +5977,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -6995,7 +6988,6 @@ "version": "6.12.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.12.0.tgz", "integrity": "sha512-pmV7NEqQej9WjizN6RSNIwf7Y+jeh9mY1JEX2WjGxJi4YZWexClhde1yz/FuvAM+cTwzchcMytu2m4I6wPkIzg==", - "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/backend/package.json b/backend/package.json index 97d0d13d..609ba702 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,6 +19,7 @@ "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.3.0", "@prisma/client": "6.12.0", + "prisma": "6.12.0", "bcryptjs": "^2.4.3", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -42,7 +43,6 @@ "@types/node": "^22.15.29", "@types/passport-jwt": "^4.0.1", "@types/uuid": "^10.0.0", - "prisma": "6.12.0", "typescript": "^5.4.5", "jest": "^29.7.0", "ts-jest": "^29.2.6", diff --git a/flutter/Dockerfile b/flutter/Dockerfile index de548d2e..aeff9756 100644 --- a/flutter/Dockerfile +++ b/flutter/Dockerfile @@ -1,9 +1,9 @@ # Stage 1 – Build Flutter web -FROM ghcr.io/cirruslabs/flutter:stable AS builder +FROM ghcr.io/cirruslabs/flutter:3.41.9 AS builder WORKDIR /app -COPY pubspec.yaml pubspec.lock* ./ +COPY pubspec.yaml pubspec.lock ./ RUN flutter pub get COPY . . diff --git a/flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart b/flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart index 1f36c4be..c8bae50f 100644 --- a/flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart +++ b/flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart @@ -291,7 +291,6 @@ class _ForegroundTile extends ConsumerWidget { String _fmtQty(double v) => formatQuantity(v); String _formatDate(String iso) => formatDateString(iso); - } } // ── Trailing action buttons ─────────────────────────────────────────────────