6 Commits eecc7ed247 ... fca18e4737

Auteur SHA1 Message Date
  Code_Uwe fca18e4737 RHL-005-fix(env): update MongoDB connection string and clarify root auth requirements il y a 1 jour
  Code_Uwe 61147c9a96 RHL-005-fix(page): remove unnecessary whitespace and improve code formatting il y a 1 jour
  Code_Uwe 952d191e48 RHL-005-refactor(auth): update database connection to use getDb and improve test mocks il y a 1 jour
  Code_Uwe 60920aeffe RHL-005-refactor(db): refactor MongoDB connection to use Mongoose and improve error handling il y a 1 jour
  Code_Uwe 71824431b3 RHL-005-fix(tests): update SESSION_SECRET to meet environment policy requirements il y a 1 jour
  Code_Uwe 82b73c21b1 RHL-005-feat(docker): add .dockerignore to exclude env files and build outputs il y a 2 jours
8 fichiers modifiés avec 66 ajouts et 29 suppressions
  1. 14 0
      .dockerignore
  2. 3 2
      .env.docker.example
  3. 4 2
      app/api/auth/login/route.js
  4. 6 5
      app/api/auth/login/route.test.js
  5. 0 2
      app/page.js
  6. 2 1
      lib/auth/session.test.js
  7. 35 15
      lib/db.js
  8. 2 2
      lib/utils.js

+ 14 - 0
.dockerignore

@@ -0,0 +1,14 @@
+# env files (never bake secrets into images)
+.env*
+!.env*.example
+
+# dependencies / build output
+node_modules
+.next
+coverage
+.git
+.DS_Store
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*

+ 3 - 2
.env.docker.example

@@ -4,8 +4,9 @@
 # NOTE: Never commit real secrets. Use placeholders in this example file.
 
 # MongoDB connection string inside the docker-compose network.
-# "mongo" should match the MongoDB service name in docker-compose.yml.
-MONGODB_URI=mongodb://mongo:27017/rhl_lieferscheine
+# "db" should match the MongoDB service name in docker-compose.yml.
+# If root auth is enabled (MONGO_INITDB_ROOT_USERNAME/PASSWORD), include credentials.
+MONGODB_URI=mongodb://root:replace-me@db:27017/rhl-lieferscheine?authSource=admin
 
 # Secret used to sign session JWT cookies.
 # Requirements: strong + random, at least 32 characters, never committed.

+ 4 - 2
app/api/auth/login/route.js

@@ -1,6 +1,7 @@
+// app/api/auth/login/route.js
 import bcrypt from "bcryptjs";
 import User from "@/models/user";
-import dbConnect from "@/lib/db";
+import { getDb } from "@/lib/db";
 import { createSession } from "@/lib/auth/session";
 
 /**
@@ -39,7 +40,8 @@ export async function POST(request) {
 
 		const normalizedUsername = username.trim().toLowerCase();
 
-		await dbConnect();
+		// Ensure DB (Mongoose) connection is established before using models.
+		await getDb();
 
 		const user = await User.findOne({ username: normalizedUsername }).exec();
 

+ 6 - 5
app/api/auth/login/route.test.js

@@ -1,9 +1,10 @@
+// app/api/auth/login/route.test.js
 import { describe, it, expect, vi, beforeEach } from "vitest";
 
 // 1) Mocks
 
 vi.mock("@/lib/db", () => ({
-	default: vi.fn(),
+	getDb: vi.fn(),
 }));
 
 vi.mock("@/models/user", () => ({
@@ -24,9 +25,9 @@ vi.mock("bcryptjs", () => {
 	};
 });
 
-// 2) Imports NACH den Mocks
+// 2) Imports AFTER the mocks
 
-import dbConnect from "@/lib/db";
+import { getDb } from "@/lib/db";
 import User from "@/models/user";
 import { createSession } from "@/lib/auth/session";
 import { compare as bcryptCompare } from "bcryptjs";
@@ -43,7 +44,7 @@ function createRequestStub(body) {
 describe("POST /api/auth/login", () => {
 	beforeEach(() => {
 		vi.clearAllMocks();
-		dbConnect.mockResolvedValue(undefined);
+		getDb.mockResolvedValue({}); // we only need it to "connect"
 	});
 
 	it("logs in successfully with correct credentials", async () => {
@@ -72,7 +73,7 @@ describe("POST /api/auth/login", () => {
 		expect(response.status).toBe(200);
 		expect(json).toEqual({ ok: true });
 
-		expect(dbConnect).toHaveBeenCalledTimes(1);
+		expect(getDb).toHaveBeenCalledTimes(1);
 		expect(User.findOne).toHaveBeenCalledWith({ username: "branchuser" });
 
 		expect(bcryptCompare).toHaveBeenCalledWith(

+ 0 - 2
app/page.js

@@ -1,10 +1,8 @@
 import { ModeToggle } from "@/components/ui/mode-toggle";
-import Image from "next/image";
 
 export default function Home() {
 	return (
 		<>
-			{" "}
 			<main className="flex min-h-screen flex-col items-center justify-center gap-4">
 				<h1 className="text-3xl font-bold">RHL Lieferscheine</h1>
 				<p className="text-muted-foreground">

+ 2 - 1
lib/auth/session.test.js

@@ -41,7 +41,8 @@ import { __cookieStore } from "next/headers";
 describe("auth session utilities", () => {
 	beforeEach(() => {
 		__cookieStore.clear();
-		process.env.SESSION_SECRET = "test-session-secret";
+		// Align tests with env policy: strong secret (>= 32 chars)
+		process.env.SESSION_SECRET = "x".repeat(64);
 		process.env.NODE_ENV = "test";
 	});
 

+ 35 - 15
lib/db.js

@@ -1,28 +1,48 @@
 // lib/db.js
-import { MongoClient } from "mongodb";
+import mongoose from "mongoose";
 
-const uri = process.env.MONGODB_URI;
+// Reuse the connection across hot reloads in dev and across route handler invocations.
+const globalForMongoose = globalThis;
 
-let client;
-let clientPromise;
+const cached =
+	globalForMongoose.__mongooseCache ||
+	(globalForMongoose.__mongooseCache = { conn: null, promise: null });
+
+async function connectMongoose() {
+	const uri = process.env.MONGODB_URI;
 
-function getClientPromise() {
 	if (!uri) {
-		// Jetzt meckern wir erst beim tatsächlichen Zugriff auf die DB
-		throw new Error("MONGODB_URI ist nicht gesetzt (Env prüfen)");
+		throw new Error("MONGODB_URI environment variable is not set");
+	}
+
+	if (cached.conn) {
+		return cached.conn;
 	}
 
-	if (!clientPromise) {
-		// In Dev-Umgebungen könnte man global._mongoClientPromise nutzen;
-		// auf dem Server reicht ein einfacher Singleton.
-		client = new MongoClient(uri);
-		clientPromise = client.connect();
+	if (!cached.promise) {
+		cached.promise = mongoose
+			.connect(uri, {
+				// Fail fast if someone queries before we are connected
+				bufferCommands: false,
+			})
+			.then((m) => m);
 	}
 
-	return clientPromise;
+	cached.conn = await cached.promise;
+	return cached.conn;
 }
 
+/**
+ * Returns the native MongoDB db handle (from the active Mongoose connection).
+ * This also ensures the Mongoose connection is established.
+ */
 export async function getDb() {
-	const client = await getClientPromise();
-	return client.db();
+	await connectMongoose();
+
+	const db = mongoose.connection?.db;
+	if (!db) {
+		throw new Error("MongoDB connection is not ready");
+	}
+
+	return db;
 }

+ 2 - 2
lib/utils.js

@@ -1,6 +1,6 @@
 import { clsx } from "clsx";
-import { twMerge } from "tailwind-merge"
+import { twMerge } from "tailwind-merge";
 
 export function cn(...inputs) {
-  return twMerge(clsx(inputs));
+	return twMerge(clsx(inputs));
 }