From ac5288aa4c1b4ef9f3c491e6330e968e5500afb7 Mon Sep 17 00:00:00 2001 From: Elias Bennour Date: Tue, 20 May 2025 01:14:08 +0200 Subject: [PATCH] fix some minor issues --- .dockerignore | 18 +++++ Dockerfile | 73 +++++++++++++++++++ next.config.ts | 1 + src/lib/prisma.ts | 2 +- .../api/admin/users/[userId]/transactions.ts | 30 ++++---- src/pages/api/auth/[...nextauth].ts | 2 +- src/pages/auth/signin.tsx | 12 +-- 7 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dd6c173 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.DS_Store +node_modules +.next +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.env +.env*.local +.env*.development +.env*.production +.vscode/ +.git/ +*.tgz +# SQLite dev databases, falls verwendet +prisma/dev.db* +prisma/dev.db-journal* +# Migrations werden normalerweise nicht im App-Image ausgeführt +prisma/migrations \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8ad2672 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,73 @@ +# Stage 1: Basis-Abhängigkeiten installieren und Prisma Client generieren +# Diese Stufe dient dazu, alle notwendigen Abhängigkeiten (inkl. devDependencies für den Build) +# zu installieren und den Prisma Client zu generieren. +FROM node:18-alpine AS base +WORKDIR /app + +# Kopiere package.json und package-lock.json (oder yarn.lock, pnpm-lock.yaml) +COPY package.json package-lock.json* ./ + +# Installiere Abhängigkeiten sauber basierend auf dem Lockfile +RUN npm ci + +# Kopiere das Prisma-Schema-Verzeichnis +COPY prisma ./prisma + +# Generiere den Prisma Client. Dieser wird für den Build-Prozess benötigt. +# Der generierte Client landet in node_modules/.prisma/client +RUN npx prisma generate + +# Stage 2: Anwendung bauen +# Diese Stufe verwendet die Abhängigkeiten und den Prisma Client aus der 'base'-Stufe, +# um die Next.js-Anwendung zu bauen. +FROM node:18-alpine AS builder +WORKDIR /app + +# Kopiere die installierten node_modules aus der 'base'-Stufe +COPY --from=base /app/node_modules ./node_modules + +# Kopiere das Prisma-Verzeichnis (Schema und ggf. generierter Client, falls außerhalb von node_modules) +# Obwohl der Client in node_modules ist, kann das Schema für den Build-Prozess referenziert werden. +COPY --from=base /app/prisma ./prisma + +# Kopiere den Rest des Anwendungscodes +COPY . . + +# Setze die Umgebungsvariable für den Produktions-Build +ENV NODE_ENV production + +# Führe den Build-Befehl aus. +# Dieser Befehl sollte die `output: 'standalone'` Konfiguration in next.config.js nutzen. +RUN npm run build + +# Stage 3: Produktions-Image +# Diese Stufe erstellt das endgültige, schlanke Image für den Betrieb der Anwendung. +FROM node:18-alpine AS runner +WORKDIR /app + +# Setze die Umgebung auf Produktion +ENV NODE_ENV production +# Setze einen Standard-Port, dieser kann zur Laufzeit durch die Umgebungsvariable PORT überschrieben werden +ENV PORT 3000 + +# Kopiere den 'standalone'-Output aus der 'builder'-Stufe. +# Dieser Ordner enthält einen minimalen server.js und nur die notwendigen node_modules. +COPY --from=builder /app/.next/standalone ./ + +# Kopiere statische Assets (.next/static) +COPY --from=builder /app/.next/static ./.next/static + +# Kopiere öffentliche Assets (public Ordner) +COPY --from=builder /app/public ./public + +# Das Prisma-Schema wird vom Prisma Client zur Laufzeit benötigt, um die +# Datenbankverbindungsinformationen aus den `datasources` zu lesen. +# Der 'standalone'-Output könnte dies nicht automatisch einbeziehen. +# Es ist sicherer, es explizit zu kopieren. +COPY --from=builder /app/prisma ./prisma + +# Gib den Port frei, auf dem die Next.js-Anwendung laufen wird +EXPOSE 3000 + +# Der Befehl zum Starten des Next.js-Servers aus dem 'standalone'-Output +CMD ["node", "server.js"] diff --git a/next.config.ts b/next.config.ts index 3915163..3d82998 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,6 +3,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ reactStrictMode: true, + output: "standalone" }; export default nextConfig; diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 91ecaa5..c8589b1 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -1,7 +1,7 @@ import { PrismaClient } from '@prisma/client'; declare global { - var prisma: PrismaClient | undefined; + let prisma: PrismaClient | undefined; } const globalForPrisma = global as typeof globalThis & { prisma?: PrismaClient }; diff --git a/src/pages/api/admin/users/[userId]/transactions.ts b/src/pages/api/admin/users/[userId]/transactions.ts index 42c9f06..2e9145b 100644 --- a/src/pages/api/admin/users/[userId]/transactions.ts +++ b/src/pages/api/admin/users/[userId]/transactions.ts @@ -1,9 +1,9 @@ // Datei: pages/api/admin/users/[userId]/transactions.ts -import type { NextApiRequest, NextApiResponse } from 'next'; -import { getServerSession } from 'next-auth/next'; -import { authOptions } from '../../../auth/[...nextauth]'; // Pfad anpassen +import type {NextApiRequest, NextApiResponse} from 'next'; +import {getServerSession} from 'next-auth/next'; +import {authOptions} from '../../../auth/[...nextauth]'; // Pfad anpassen import prisma from '../../../../../lib/prisma'; // Pfad anpassen -import { Transaction, User } from '@prisma/client'; // Importiere relevante Typen +import {Prisma, Transaction} from '@prisma/client'; // Importiere relevante Typen // Erweitere den Transaction-Typ, um optional den auslösenden Admin einzuschließen type TransactionWithAdmin = Transaction & { @@ -33,26 +33,26 @@ export default async function handler( ) { if (req.method !== 'GET') { res.setHeader('Allow', ['GET']); - return res.status(405).json({ message: `Method ${req.method} Not Allowed` }); + return res.status(405).json({message: `Method ${req.method} Not Allowed`}); } // Admin-Session abrufen und validieren const session = await getServerSession(req, res, authOptions); if (!session || !session.user || session.user.role !== 'admin') { - return res.status(403).json({ message: 'Zugriff verweigert. Nur für Administratoren.' }); + return res.status(403).json({message: 'Zugriff verweigert. Nur für Administratoren.'}); } // Zielbenutzer-ID aus der URL extrahieren - const { userId: targetUserId } = req.query; + const {userId: targetUserId} = req.query; if (typeof targetUserId !== 'string') { - return res.status(400).json({ message: 'Ungültige Benutzer-ID in der URL.' }); + return res.status(400).json({message: 'Ungültige Benutzer-ID in der URL.'}); } try { // Zuerst den Zielbenutzer finden, um sicherzustellen, dass er existiert // und um seine Details in der Antwort mitzugeben. const targetUser = await prisma.user.findUnique({ - where: { id: targetUserId }, + where: {id: targetUserId}, select: { id: true, name: true, @@ -61,7 +61,7 @@ export default async function handler( }); if (!targetUser) { - return res.status(404).json({ message: 'Zielbenutzer nicht gefunden.' }); + return res.status(404).json({message: 'Zielbenutzer nicht gefunden.'}); } // Transaktionen für den Zielbenutzer abrufen @@ -93,11 +93,13 @@ export default async function handler( totalTransactions: transactions.length, }); - } catch (error: any) { + } catch (error: unknown) { console.error(`Fehler beim Abrufen des Transaktionsverlaufs für Benutzer ${targetUserId}:`, error); - if (error.code === 'P2025') { // Prisma: Record to query not found (kann bei Relationen auftreten) - return res.status(404).json({ message: 'Fehler beim Laden der zugehörigen Daten (Prisma P2025).' }); + if (error instanceof Prisma.PrismaClientKnownRequestError) { + if (error.code === 'P2025') { // Prisma: Record to query not found (kann bei Relationen auftreten) + return res.status(404).json({message: 'Fehler beim Laden der zugehörigen Daten (Prisma P2025).'}); + } } - return res.status(500).json({ message: 'Interner Serverfehler beim Abrufen des Transaktionsverlaufs.' }); + return res.status(500).json({message: 'Interner Serverfehler beim Abrufen des Transaktionsverlaufs.'}); } } diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 3b5ca00..0e6ea72 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -19,7 +19,7 @@ export const authOptions: AuthOptions = { email: { label: "E-Mail", type: "email", placeholder: "user@example.com" }, password: { label: "Passwort", type: "password" } }, - async authorize(credentials, req) { + async authorize(credentials) { if (!credentials?.email || !credentials.password) { throw new Error("E-Mail und Passwort sind erforderlich."); } diff --git a/src/pages/auth/signin.tsx b/src/pages/auth/signin.tsx index eae1e5b..8bc4b29 100644 --- a/src/pages/auth/signin.tsx +++ b/src/pages/auth/signin.tsx @@ -6,15 +6,16 @@ import { useEffect, useState } from 'react'; import type { ClientSafeProvider, LiteralUnion } from 'next-auth/react'; import type { ParsedUrlQuery } from 'querystring'; // Für GetServerSidePropsContext import type { PreviewData } from 'next/dist/types'; -import {BuiltInProviderType} from "next-auth/providers/index"; // Für GetServerSidePropsContext +import {BuiltInProviderType} from "next-auth/providers/index"; +import Link from "next/link"; // Für GetServerSidePropsContext interface SignInProps { providers: Record, ClientSafeProvider> | null; csrfToken: string | undefined; } -const SignInPage: NextPage = ({ providers, csrfToken }) => { - const { data: session, status } = useSession(); +const SignInPage: NextPage = ({ providers}) => { + const {status } = useSession(); const router = useRouter(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -186,10 +187,9 @@ const SignInPage: NextPage = ({ providers, csrfToken }) => {

Noch kein Konto?{' '} - + Registrieren - - {/* TODO: Registrierungsseite erstellen */} +