Initialize Stage 0: Next.js 16 scaffold with auth and role-based routing

- Next.js 16.2.2 + React 19 + TypeScript + Tailwind v4
- Better Auth with email/password and role system (student/curator/admin)
- Prisma 7 schema: User, Session, Account, Verification + full LMS model
- Role-based dashboards: student /dashboard, curator /curator/dashboard, admin /admin/dashboard
- Auth pages: login, register, verify-email
- Better Auth API route handler
- Middleware for route protection
- Docker Compose with PostgreSQL 16
- Seed script with test users (admin/curator/student)
- CLAUDE.md and ROADMAP.md project documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 10:32:37 +05:00
commit 80ca4b2d9d
41 changed files with 10138 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
"use client";
import { createAuthClient } from "better-auth/react";
import { adminClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
plugins: [adminClient()],
});
export const { signIn, signOut, signUp, useSession } = authClient;
+36
View File
@@ -0,0 +1,36 @@
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { admin } from "better-auth/plugins";
import { prisma } from "./prisma";
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
plugins: [
admin({
defaultRole: "student",
adminRoles: ["admin"],
}),
],
trustedOrigins: [
process.env.BETTER_AUTH_URL ?? "http://localhost:3000",
"https://school.second-brain.ru",
],
user: {
additionalFields: {
role: {
type: "string",
defaultValue: "student",
input: false,
},
},
},
});
export type Session = typeof auth.$Infer.Session;
export type User = typeof auth.$Infer.Session.user;
+13
View File
@@ -0,0 +1,13 @@
import { PrismaClient } from "../generated/prisma";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === "development" ? ["error", "warn"] : ["error"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}