feat: initialize Next.js project with Prisma, authentication, and rankings page
This commit is contained in:
+131
@@ -0,0 +1,131 @@
|
||||
import NextAuth, { DefaultSession, DefaultUser } from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials"
|
||||
import { prisma } from "./prisma"
|
||||
import bcrypt from "bcryptjs"
|
||||
|
||||
// Extend the built-in session and user types
|
||||
declare module "next-auth" {
|
||||
interface Session {
|
||||
user: {
|
||||
id: string
|
||||
playerId: number
|
||||
role: string
|
||||
} & DefaultSession["user"]
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
playerId: number
|
||||
role: string
|
||||
}
|
||||
}
|
||||
|
||||
export const {
|
||||
handlers: { GET, POST },
|
||||
auth,
|
||||
signIn,
|
||||
signOut,
|
||||
} = NextAuth({
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
email: { label: "Email", type: "email" },
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
if (!credentials?.email || !credentials?.password) {
|
||||
return null
|
||||
}
|
||||
|
||||
const email = credentials.email as string
|
||||
const password = credentials.password as string
|
||||
|
||||
// Find user by email
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
include: { player: true },
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Check if account is locked
|
||||
if (user.lockedUntil && user.lockedUntil > new Date()) {
|
||||
throw new Error("Account locked. Please try again later.")
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const isValid = await bcrypt.compare(password, user.passwordDigest)
|
||||
|
||||
if (!isValid) {
|
||||
// Increment failed login attempts
|
||||
const failedAttempts = (user.failedLoginAttempts || 0) + 1
|
||||
|
||||
if (failedAttempts >= 5) {
|
||||
// Lock account for 15 minutes
|
||||
const lockedUntil = new Date(Date.now() + 15 * 60 * 1000)
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { failedLoginAttempts: failedAttempts, lockedUntil },
|
||||
})
|
||||
throw new Error("Account locked after 5 failed attempts. Please try again in 15 minutes.")
|
||||
} else {
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { failedLoginAttempts: failedAttempts },
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Successful login - update last login and reset failed attempts
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
lastLoginAt: new Date(),
|
||||
failedLoginAttempts: 0,
|
||||
lockedUntil: null,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
id: user.id.toString(),
|
||||
playerId: user.playerId,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
name: user.player.name,
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token, user }) {
|
||||
if (user) {
|
||||
token.id = user.id
|
||||
token.playerId = user.playerId
|
||||
token.role = user.role
|
||||
}
|
||||
return token
|
||||
},
|
||||
async session({ session, token }) {
|
||||
if (session.user) {
|
||||
session.user.id = token.id as string
|
||||
session.user.playerId = token.playerId as number
|
||||
session.user.role = token.role as string
|
||||
}
|
||||
return session
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
signIn: "/auth/login",
|
||||
signOut: "/auth/logout",
|
||||
error: "/auth/error",
|
||||
},
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||
},
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
||||
Reference in New Issue
Block a user