initial commit

This commit is contained in:
Kar k1
2025-08-30 18:18:57 +05:30
commit 7219108342
270 changed files with 70221 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
const ForgotPasswordSchema = z.object({
email: z.string().email('Please enter a valid email address'),
})
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate the request body
const validatedData = ForgotPasswordSchema.parse(body)
// Simulate processing delay
await new Promise(resolve => setTimeout(resolve, 1000))
// Dummy API - Always return an error for demonstration
// You can change this behavior for testing different scenarios
const email = validatedData.email
// Simulate different error scenarios based on email
if (email === 'test@example.com') {
return NextResponse.json(
{
success: false,
error: {
message: 'Email address not found in our system',
code: 'EMAIL_NOT_FOUND'
}
},
{ status: 404 }
)
}
if (email.includes('blocked')) {
return NextResponse.json(
{
success: false,
error: {
message: 'This email address has been temporarily blocked',
code: 'EMAIL_BLOCKED'
}
},
{ status: 429 }
)
}
if (email.includes('invalid')) {
return NextResponse.json(
{
success: false,
error: {
message: 'Invalid email format',
code: 'INVALID_EMAIL'
}
},
{ status: 400 }
)
}
// Default error response (500 Internal Server Error)
return NextResponse.json(
{
success: false,
error: {
message: 'Unable to process password reset request at this time. Please try again later.',
code: 'SERVER_ERROR'
}
},
{ status: 500 }
)
// Uncomment below for success response (when you want to test success state)
/*
return NextResponse.json(
{
success: true,
message: 'Password reset email sent successfully',
data: {
email: validatedData.email,
resetTokenExpiry: Date.now() + 3600000 // 1 hour from now
}
},
{ status: 200 }
)
*/
} catch (error) {
console.error('Forgot password API error:', error)
// Handle validation errors
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: {
message: 'Invalid request data',
code: 'VALIDATION_ERROR',
details: error.issues
}
},
{ status: 400 }
)
}
// Handle other errors
return NextResponse.json(
{
success: false,
error: {
message: 'An unexpected error occurred',
code: 'INTERNAL_ERROR'
}
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,113 @@
import { NextRequest, NextResponse } from 'next/server'
import { getGoogleUser } from '@/lib/google-oauth'
import { generateTokens } from '@/lib/jwt'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
import { appConfig } from '@/lib/env'
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const code = searchParams.get('code')
const state = searchParams.get('state')
const error = searchParams.get('error')
// Handle OAuth errors
if (error) {
return NextResponse.redirect(
new URL(`/auth?error=${encodeURIComponent(error)}`, appConfig.appUrl)
)
}
// Validate required parameters
if (!code || state !== 'google_oauth') {
return NextResponse.redirect(new URL('/auth?error=invalid_oauth_callback', appConfig.appUrl))
}
// Get user info from Google
const googleUser = await getGoogleUser(code)
// Connect to database
await connectDB()
// Check if user exists
let user = await User.findOne({
$or: [
{ email: googleUser.email.toLowerCase() },
{ providerId: googleUser.id, provider: 'google' },
],
})
if (!user) {
// Create new user
user = new User({
email: googleUser.email.toLowerCase(),
name: googleUser.name,
provider: 'google',
providerId: googleUser.id,
avatar: googleUser.picture || undefined,
isVerified: googleUser.verified_email,
lastLogin: new Date(),
})
} else {
// Update existing user with Google info (preserve original provider)
// Only link Google if user doesn't already have a local account
if (user.provider !== 'local') {
user.provider = 'google'
user.providerId = googleUser.id
} else {
// For local users, just add Google info without changing provider
if (!user.providerId) {
user.providerId = googleUser.id
}
}
// Update other info safely
user.name = googleUser.name
user.isVerified = googleUser.verified_email || user.isVerified
user.lastLogin = new Date()
// Update avatar only if user doesn't have one
if (googleUser.picture && !user.avatar) {
user.avatar = googleUser.picture
}
}
// Generate tokens
const { accessToken, refreshToken } = generateTokens({
userId: user._id.toString(),
email: user.email,
role: user.role,
})
// Update user's refresh token
user.refreshToken = refreshToken
await user.save()
// Create redirect response using the public app URL
const redirectURL = new URL('/dashboard', appConfig.appUrl)
const response = NextResponse.redirect(redirectURL)
// Set HTTP-only cookies
response.cookies.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60, // 15 minutes
path: '/',
})
response.cookies.set('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, // 7 days
path: '/',
})
return response
} catch (error) {
console.error('Google OAuth callback error:', error)
return NextResponse.redirect(new URL('/auth?error=oauth_callback_failed', appConfig.appUrl))
}
}

View File

@@ -0,0 +1,22 @@
import { NextRequest, NextResponse } from 'next/server'
import { getGoogleAuthURL } from '@/lib/google-oauth'
export async function GET(request: NextRequest) {
try {
const authURL = getGoogleAuthURL()
return NextResponse.json({
success: true,
data: { authURL },
})
} catch (error) {
console.error('Google OAuth URL generation error:', error)
return NextResponse.json(
{
success: false,
error: { message: 'Failed to generate Google auth URL', code: 'OAUTH_ERROR' },
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,98 @@
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
import { generateTokens } from '@/lib/jwt'
const LoginSchema = z.object({
emailOrId: z.string().min(1, 'Email or Silicon ID is required'),
password: z.string().min(1, 'Password is required'),
rememberMe: z.boolean().optional(),
})
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate input
const validatedData = LoginSchema.parse(body)
// Connect to database
await connectDB()
// Find user by email or Silicon ID
const emailOrId = validatedData.emailOrId
const user = await User.findOne({
$or: [{ email: emailOrId.toLowerCase() }, { siliconId: emailOrId }],
})
if (!user) {
return NextResponse.json(
{ success: false, error: { message: 'Invalid credentials', code: 'INVALID_CREDENTIALS' } },
{ status: 401 }
)
}
// Check password
const isPasswordValid = await user.comparePassword(validatedData.password)
if (!isPasswordValid) {
return NextResponse.json(
{ success: false, error: { message: 'Invalid credentials', code: 'INVALID_CREDENTIALS' } },
{ status: 401 }
)
}
// Generate tokens
const { accessToken, refreshToken } = generateTokens({
userId: user._id.toString(),
email: user.email,
role: user.role,
})
// Update user's refresh token and last login
user.refreshToken = refreshToken
user.lastLogin = new Date()
await user.save()
// Create response with tokens
const response = NextResponse.json({
success: true,
data: {
user: user.toJSON(),
accessToken,
},
})
// Set HTTP-only cookies
response.cookies.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60, // 15 minutes
path: '/',
})
response.cookies.set('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, // 7 days
path: '/',
})
return response
} catch (error) {
console.error('Login error:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{ success: false, error: { message: error.issues[0].message, code: 'VALIDATION_ERROR' } },
{ status: 400 }
)
}
return NextResponse.json(
{ success: false, error: { message: 'Internal server error', code: 'INTERNAL_ERROR' } },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,62 @@
import { NextRequest, NextResponse } from 'next/server'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
import { verifyRefreshToken } from '@/lib/jwt'
export async function POST(request: NextRequest) {
try {
// Get refresh token from cookie
const refreshToken = request.cookies.get('refreshToken')?.value
if (refreshToken) {
// Verify and decode the refresh token to get user ID
const payload = verifyRefreshToken(refreshToken)
if (payload) {
// Connect to database and remove refresh token
await connectDB()
await User.findByIdAndUpdate(payload.userId, {
$unset: { refreshToken: 1 },
})
}
}
// Create response
const response = NextResponse.json({
success: true,
data: { message: 'Logged out successfully' },
})
// Clear cookies
response.cookies.set('accessToken', '', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 0,
path: '/',
})
response.cookies.set('refreshToken', '', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 0,
path: '/',
})
return response
} catch (error) {
console.error('Logout error:', error)
// Even if there's an error, we should still clear the cookies
const response = NextResponse.json({
success: true,
data: { message: 'Logged out successfully' },
})
response.cookies.set('accessToken', '', { maxAge: 0, path: '/' })
response.cookies.set('refreshToken', '', { maxAge: 0, path: '/' })
return response
}
}

33
app/api/auth/me/route.ts Normal file
View File

@@ -0,0 +1,33 @@
import { NextRequest, NextResponse } from 'next/server'
import { withAuth } from '@/lib/auth-middleware'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
export const GET = withAuth(async (request: NextRequest & { user?: any }) => {
try {
// Connect to database
await connectDB()
// Get user details
const user = await User.findById(request.user.userId).select('-password -refreshToken')
if (!user) {
return NextResponse.json(
{ success: false, error: { message: 'User not found', code: 'USER_NOT_FOUND' } },
{ status: 404 }
)
}
return NextResponse.json({
success: true,
data: { user: user.toJSON() },
})
} catch (error) {
console.error('Get user info error:', error)
return NextResponse.json(
{ success: false, error: { message: 'Internal server error', code: 'INTERNAL_ERROR' } },
{ status: 500 }
)
}
})

View File

@@ -0,0 +1,99 @@
import { NextRequest, NextResponse } from 'next/server'
import connectDB from '@/lib/mongodb'
import { User } from '@/models/user'
import { verifyRefreshToken, generateTokens } from '@/lib/jwt'
export async function POST(request: NextRequest) {
try {
// Get refresh token from cookie
const refreshToken = request.cookies.get('refreshToken')?.value
if (!refreshToken) {
return NextResponse.json(
{
success: false,
error: { message: 'No refresh token provided', code: 'NO_REFRESH_TOKEN' },
},
{ status: 401 }
)
}
// Verify refresh token
const payload = verifyRefreshToken(refreshToken)
if (!payload) {
return NextResponse.json(
{
success: false,
error: { message: 'Invalid refresh token', code: 'INVALID_REFRESH_TOKEN' },
},
{ status: 401 }
)
}
// Connect to database and find user
await connectDB()
const user = await User.findById(payload.userId)
// Check if user exists and refresh token matches
if (!user) {
return NextResponse.json(
{ success: false, error: { message: 'User not found', code: 'USER_NOT_FOUND' } },
{ status: 401 }
)
}
// Verify the stored refresh token matches (both are JWT tokens, so direct comparison is valid)
if (user.refreshToken !== refreshToken) {
return NextResponse.json(
{ success: false, error: { message: 'Refresh token mismatch', code: 'TOKEN_MISMATCH' } },
{ status: 401 }
)
}
// Generate new tokens
const { accessToken, refreshToken: newRefreshToken } = generateTokens({
userId: user._id.toString(),
email: user.email,
role: user.role,
})
// Update user's refresh token
user.refreshToken = newRefreshToken
await user.save()
// Create response with new tokens
const response = NextResponse.json({
success: true,
data: {
accessToken,
user: user.toJSON(),
},
})
// Set new cookies
response.cookies.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60, // 15 minutes
path: '/',
})
response.cookies.set('refreshToken', newRefreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, // 7 days
path: '/',
})
return response
} catch (error) {
console.error('Token refresh error:', error)
return NextResponse.json(
{ success: false, error: { message: 'Internal server error', code: 'INTERNAL_ERROR' } },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,109 @@
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import connectDB from '@/lib/mongodb'
import { User, UserSchema } from '@/models/user'
import { generateTokens } from '@/lib/jwt'
import { generateSiliconId } from '@/lib/siliconId'
const RegisterSchema = UserSchema.pick({
email: true,
name: true,
password: true,
})
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate input
const validatedData = RegisterSchema.parse(body)
// Connect to database
await connectDB()
// Check if user already exists
const existingUser = await User.findOne({ email: validatedData.email.toLowerCase() })
if (existingUser) {
return NextResponse.json(
{ success: false, error: { message: 'User already exists', code: 'USER_EXISTS' } },
{ status: 409 }
)
}
// Generate tokens first
const tempUser = {
email: validatedData.email.toLowerCase(),
name: validatedData.name,
role: 'user' as const,
}
// Generate unique Silicon ID
const siliconId = generateSiliconId()
console.log('Generated siliconId:', siliconId)
// Create new user
const user = new User({
email: tempUser.email,
name: tempUser.name,
password: validatedData.password,
siliconId: siliconId,
provider: 'local',
})
console.log('User before save:', JSON.stringify(user.toObject(), null, 2))
await user.save()
console.log('user after save:', user)
// Generate tokens with actual user ID
const { accessToken, refreshToken } = generateTokens({
userId: user._id.toString(),
email: user.email,
role: user.role,
})
// Update user with refresh token (single save)
user.refreshToken = refreshToken
await user.save()
// Create response with tokens
const response = NextResponse.json({
success: true,
data: {
user: user.toJSON(),
accessToken,
},
})
// Set HTTP-only cookies
response.cookies.set('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60, // 15 minutes
path: '/',
})
response.cookies.set('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60, // 7 days
path: '/',
})
return response
} catch (error) {
console.error('Registration error:', error)
if (error instanceof z.ZodError) {
return NextResponse.json(
{ success: false, error: { message: error.issues[0].message, code: 'VALIDATION_ERROR' } },
{ status: 400 }
)
}
return NextResponse.json(
{ success: false, error: { message: 'Internal server error', code: 'INTERNAL_ERROR' } },
{ status: 500 }
)
}
}