feat(db): integrate Drizzle ORM with SQLite
This commit is contained in:
parent
fd728634ce
commit
f5fc0ac256
9 changed files with 1809 additions and 16 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -39,3 +39,8 @@ yarn-error.log*
|
|||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# SQLite Datenbanken
|
||||
*.db
|
||||
*.db-journal
|
||||
/db/migrations/
|
||||
11
README.md
11
README.md
|
|
@ -7,14 +7,14 @@ A modern, high-performance Kanban & Workflow web application designed to help yo
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 🛠️ 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.
|
||||
|
|
|
|||
|
|
@ -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
78
auth.ts
|
|
@ -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
15
db/index.ts
Normal 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
28
db/schema.ts
Normal 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
18
drizzle.config.ts
Normal 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
1663
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue