feat(db): integrate Drizzle ORM with SQLite

This commit is contained in:
Chneemann 2026-08-03 18:07:02 +02:00
parent fd728634ce
commit f5fc0ac256
No known key found for this signature in database
9 changed files with 1809 additions and 16 deletions

5
.gitignore vendored
View file

@ -39,3 +39,8 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
# SQLite Datenbanken
*.db
*.db-journal
/db/migrations/

View file

@ -7,14 +7,14 @@ A modern, high-performance Kanban & Workflow web application designed to help yo
![TypeScript](https://img.shields.io/badge/TypeScript-5-blue?style=flat-square&logo=typescript)
![Tailwind CSS](https://img.shields.io/badge/Tailwind-v4-38bdf8?style=flat-square&logo=tailwind-css)
![Auth.js](https://img.shields.io/badge/Auth.js-v5-purple?style=flat-square)
![PostgreSQL](https://img.shields.io/badge/PostgreSQL-16-4169E1?style=flat-square&logo=postgresql)
![Drizzle ORM](https://img.shields.io/badge/Drizzle-ORM-C5F74F?style=flat-square&logo=drizzle)
## 🛠️ Tech Stack
- **Framework:** [Next.js 16](https://nextjs.org/) (App Router, Route Groups)
- **UI & Styling:** [React 19](https://react.dev/), [Tailwind CSS v4](https://tailwindcss.com/)
- **Authentication:** [Auth.js v5 (NextAuth)](https://authjs.dev/) with Credentials Provider
- **Database & State:** PostgreSQL / Prisma (in progress)
- **Authentication:** [Auth.js v5 (NextAuth)](https://authjs.dev/) with Credentials Provider & bcrypt hashing
- **Database & ORM:** [Drizzle ORM](https://orm.drizzle.team/) & Drizzle Kit
## 📂 Architecture & Structure
@ -22,8 +22,9 @@ The project uses Next.js Route Groups to separate public and protected applicati
- `app/(auth)/` — Public authentication routes (Login, Register)
- `app/(app)/` — Protected workspace & dashboard routes (Sidebar, Header, Kanban Board)
- `app/api/` — Backend API endpoints & Auth handlers
- `app/api/` — Backend API endpoints & Auth handlers (`/api/auth/register`, `[...nextauth]`)
- `db/` — Database schema definitions, migrations, and Drizzle configuration (`drizzle.config.ts`)
## 🎯 Current Status
_In Progress_ — Core layout structure, responsive navigation, and authentication (Auth.js v5) are fully set up. Next up: Kanban board columns and task management features.
_In Progress_ — Core layout structure, responsive navigation, and authentication (Auth.js v5) are implemented. Next up: Migration from SQLite to PostgreSQL via Drizzle ORM.

View file

@ -111,7 +111,7 @@ export default function LoginPage() {
<button
type="submit"
disabled={loading}
className="w-full py-2 text-sm font-medium rounded-lg bg-foreground text-background hover:opacity-70 transition-opacity disabled:opacity-20 cursor-pointer"
className="w-full py-2 text-sm font-medium rounded-lg bg-foreground text-background hover:opacity-70 transition-opacity disabled:opacity-20 cursor-pointer disabled:cursor-auto"
>
{loading ? "Logging in..." : "Sign In"}
</button>

78
auth.ts
View file

@ -1,26 +1,88 @@
/**
* @file auth.ts
* @description NextAuth configuration file setting up credentials authentication, database lookups via Drizzle, JWT sessions, and custom pages.
*/
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { db } from "@/db";
import { usersTable } from "@/db/schema";
import { eq } from "drizzle-orm";
import bcrypt from "bcryptjs";
/**
* Exports NextAuth configuration handlers, authentication state checkers, and auth methods.
*/
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Credentials({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
/**
* Authorizes a user by verifying their email and comparing hashed passwords from the database.
*
* @async
* @param {Record<string, any>} [credentials] - The user credentials submitted via the sign-in form.
* @returns {Promise<{ id: string; email: string } | null>} The authenticated user object or null if authorization fails.
*/
async authorize(credentials) {
if (
credentials?.email === "test@flowstate.io" &&
credentials?.password === "password"
) {
return { id: "1", name: "Charlotte W.", email: "test@flowstate.io" };
}
return null;
if (!credentials?.email || !credentials?.password) return null;
const email = credentials.email as string;
const password = credentials.password as string;
const user = await db.query.usersTable.findFirst({
where: eq(usersTable.email, email),
});
if (!user || !user.password) return null;
const passwordsMatch = await bcrypt.compare(password, user.password);
if (!passwordsMatch) return null;
return { id: String(user.id), email: user.email };
},
}),
],
pages: {
signIn: "/login",
},
session: {
strategy: "jwt",
},
callbacks: {
/**
* Adds the user ID to the JWT token upon initial sign-in.
*
* @async
* @param {Object} params - The JWT callback parameters.
* @param {import("next-auth/jwt").JWT} params.token - The current JWT token.
* @param {import("next-auth").User} [params.user] - The authenticated user object.
* @returns {Promise<import("next-auth/jwt").JWT>} The updated JWT token.
*/
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
/**
* Injects the user ID from the JWT token into the client session object.
*
* @async
* @param {Object} params - The session callback parameters.
* @param {import("next-auth").Session} params.session - The current session object.
* @param {import("next-auth/jwt").JWT} params.token - The decoded JWT token.
* @returns {Promise<import("next-auth").Session>} The updated session object.
*/
async session({ session, token }) {
if (token && session.user) {
session.user.id = token.id as string;
}
return session;
},
},
});

15
db/index.ts Normal file
View file

@ -0,0 +1,15 @@
/**
* @file db/index.ts (oder ähnlich)
* @description Initializes the SQLite database connection using better-sqlite3 and sets up the Drizzle ORM instance with the provided schema.
*/
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import * as schema from "./schema";
const sqlite = new Database("./sqlite.db");
/**
* The Drizzle ORM database instance configured for SQLite with type definitions from the schema.
*/
export const db = drizzle(sqlite, { schema });

28
db/schema.ts Normal file
View file

@ -0,0 +1,28 @@
/**
* @file db/schema.ts (oder ähnlich)
* @description Defines the SQLite database schema for users and exports related TypeScript types using Drizzle ORM.
*/
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
/**
* Database table definition for application users.
*/
export const usersTable = sqliteTable("users", {
id: integer("id").primaryKey({ autoIncrement: true }),
email: text("email").notNull().unique(),
password: text("password").notNull(),
createdAt: integer("created_at", { mode: "timestamp" })
.$defaultFn(() => new Date())
.notNull(),
});
/**
* Represents a user record selected from the database.
*/
export type User = typeof usersTable.$inferSelect;
/**
* Represents a new user object required for insertion into the database.
*/
export type NewUser = typeof usersTable.$inferInsert;

18
drizzle.config.ts Normal file
View file

@ -0,0 +1,18 @@
/**
* @file drizzle.config.ts
* @description Drizzle Kit configuration file specifying schema locations, migration output directories, and database connection credentials for SQLite.
*/
import { defineConfig } from "drizzle-kit";
/**
* Configuration object for Drizzle Kit CLI commands (migrations, introspection, studio).
*/
export default defineConfig({
schema: "./db/schema.ts",
out: "./db/migrations",
dialect: "sqlite",
dbCredentials: {
url: "./sqlite.db",
},
});

1663
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,9 @@
"lint": "eslint"
},
"dependencies": {
"bcryptjs": "^3.0.3",
"better-sqlite3": "^13.0.2",
"drizzle-orm": "^0.45.2",
"lucide-react": "^1.28.0",
"next": "16.2.12",
"next-auth": "^5.0.0-beta.32",
@ -17,9 +20,11 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^9.6.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "^0.31.10",
"eslint": "^9",
"eslint-config-next": "16.2.12",
"tailwindcss": "^4",