fix some minor issues

This commit is contained in:
Elias Bennour 2025-05-20 01:14:08 +02:00
parent 3141ba60f4
commit ac5288aa4c
7 changed files with 116 additions and 22 deletions

18
.dockerignore Normal file
View File

@ -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

73
Dockerfile Normal file
View File

@ -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"]

View File

@ -3,6 +3,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
reactStrictMode: true,
output: "standalone"
};
export default nextConfig;

View File

@ -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 };

View File

@ -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.'});
}
}

View File

@ -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.");
}

View File

@ -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<LiteralUnion<BuiltInProviderType, string>, ClientSafeProvider> | null;
csrfToken: string | undefined;
}
const SignInPage: NextPage<SignInProps> = ({ providers, csrfToken }) => {
const { data: session, status } = useSession();
const SignInPage: NextPage<SignInProps> = ({ providers}) => {
const {status } = useSession();
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
@ -186,10 +187,9 @@ const SignInPage: NextPage<SignInProps> = ({ providers, csrfToken }) => {
<div className="mt-6 text-center text-sm">
<p className="text-gray-600">
Noch kein Konto?{' '}
<a href="/auth/signup" className="font-medium text-indigo-600 hover:text-indigo-500">
<Link href="/auth/signup" className="font-medium text-indigo-600 hover:text-indigo-500">
Registrieren
</a>
{/* TODO: Registrierungsseite erstellen */}
</Link>
</p>
</div>
</div>