now working login and register

main
Suvodip 2025-03-24 11:30:32 +05:30
parent bf940e80fb
commit f633208d8e
11 changed files with 192 additions and 63 deletions

View File

@ -21,6 +21,7 @@
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"lucide-react": "^0.475.0",
"mongoose": "^8.12.2",
"next": "^15.2.0",
@ -471,6 +472,8 @@
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
"dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],

View File

@ -6,8 +6,8 @@
publish = ".next"
[build.environment]
NETLIFY_NEXT_PLUGIN_SKIP = "true"
NEXT_PUBLIC_BASE_URL = "/"
NODE_VERSION = "18.17.0"
[[plugins]]
package = "@netlify/plugin-nextjs"

13
package-lock.json generated
View File

@ -25,6 +25,7 @@
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"lucide-react": "^0.475.0",
"mongoose": "^8.12.2",
"next": "^15.2.0",
@ -3862,6 +3863,18 @@
"csstype": "^3.0.2"
}
},
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",

View File

@ -26,6 +26,7 @@
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"lucide-react": "^0.475.0",
"mongoose": "^8.12.2",
"next": "^15.2.0",

View File

@ -10,7 +10,7 @@ const DEMO_USERS = [
id: "1",
name: "Admin User",
email: "admin@example.com",
password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123
password: "password123", // Plain text for demo
role: "admin",
isActive: true
},
@ -18,7 +18,7 @@ const DEMO_USERS = [
id: "2",
name: "HR User",
email: "hr@example.com",
password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123
password: "password123", // Plain text for demo
role: "hr",
isActive: true
},
@ -26,7 +26,7 @@ const DEMO_USERS = [
id: "3",
name: "Inactive HR User",
email: "inactive@example.com",
password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123
password: "password123", // Plain text for demo
role: "hr",
isActive: false
}
@ -42,71 +42,89 @@ export const authOptions: AuthOptions = {
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
console.log("Missing credentials");
return null;
}
try {
// First try with MongoDB
try {
await connectToDatabase();
const user = await User.findOne({ email: credentials.email });
if (user) {
// Check if user is active
if (!user.isActive) {
throw new Error("Account is inactive");
}
console.log(`Attempting login for: ${credentials.email}`);
const isPasswordValid = await bcrypt.compare(
credentials.password,
user.password
);
if (isPasswordValid) {
return {
id: user._id.toString(),
email: user.email,
name: user.name,
role: user.role,
isActive: user.isActive
};
}
}
} catch (error) {
console.log("MongoDB connection error, falling back to demo users");
}
// Fallback to demo users if MongoDB fails
// First check demo users directly with plain text comparison
const demoUser = DEMO_USERS.find(
(user) => user.email === credentials.email
);
if (!demoUser) {
return null;
if (demoUser) {
console.log("Found demo user");
// Check if demo user is active
if (!demoUser.isActive) {
console.log("Demo user found but inactive");
throw new Error("Account is inactive");
}
// Direct comparison for demo users
if (credentials.password === demoUser.password) {
console.log("Demo user password matched");
return {
id: demoUser.id,
email: demoUser.email,
name: demoUser.name,
role: demoUser.role,
isActive: demoUser.isActive
};
} else {
console.log("Invalid password for demo user");
return null;
}
}
// Check if user is active
if (!demoUser.isActive) {
throw new Error("Account is inactive");
// If no demo user found, try with MongoDB
let dbUser = null;
try {
await connectToDatabase();
dbUser = await User.findOne({ email: credentials.email });
console.log(`MongoDB connected, user found:`, !!dbUser);
} catch (dbError) {
console.error("MongoDB connection error:", dbError);
return null; // If no demo user and MongoDB fails, authentication fails
}
const isPasswordValid = await bcrypt.compare(
credentials.password,
demoUser.password
);
// If MongoDB user exists
if (dbUser) {
// Check if user is active
if (!dbUser.isActive) {
console.log("MongoDB user found but inactive");
throw new Error("Account is inactive");
}
if (!isPasswordValid) {
return null;
const isPasswordValid = await bcrypt.compare(
credentials.password,
dbUser.password
);
console.log(`Password validation for MongoDB user: ${isPasswordValid}`);
if (isPasswordValid) {
return {
id: dbUser._id.toString(),
email: dbUser.email,
name: dbUser.name,
role: dbUser.role,
isActive: dbUser.isActive
};
} else {
console.log("Invalid password for MongoDB user");
return null;
}
}
return {
id: demoUser.id,
email: demoUser.email,
name: demoUser.name,
role: demoUser.role,
isActive: demoUser.isActive
};
// No user found in demo or database
console.log("User not found in demo users or database");
return null;
} catch (error) {
console.error("Auth error:", error);
console.error("Authentication error:", error);
throw error; // Propagate errors like 'Account is inactive'
}
},
@ -138,7 +156,8 @@ export const authOptions: AuthOptions = {
strategy: "jwt",
maxAge: 24 * 60 * 60, // 24 hours
},
secret: process.env.NEXTAUTH_SECRET,
secret: process.env.NEXTAUTH_SECRET || "a-default-secret-for-development-only",
debug: process.env.NODE_ENV !== "production",
};
const handler = NextAuth(authOptions);

View File

@ -19,13 +19,13 @@ export async function POST(request: NextRequest) {
}
// Hash the password
const hashedPassword = await bcrypt.hash(password, 12);
// const hashedPassword = await bcrypt.hash(password, 12);
// Create the user
const user = await User.create({
name,
email,
password: hashedPassword,
password,
role,
});

View File

@ -7,7 +7,7 @@ import { signIn } from "next-auth/react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";
@ -25,6 +25,7 @@ export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const [isLoading, setIsLoading] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
// Form definition
const form = useForm<LoginFormValues>({
@ -38,6 +39,7 @@ export default function LoginPage() {
// Handle form submission
const onSubmit = async (data: LoginFormValues) => {
setIsLoading(true);
setLoginError(null);
try {
const result = await signIn("credentials", {
@ -45,11 +47,13 @@ export default function LoginPage() {
password: data.password,
redirect: false,
});
console.log('result', result);
if (result?.error) {
if (result.error === "Account is inactive") {
setLoginError("This account has been disabled by an administrator");
toast.error("This account has been disabled by an administrator");
} else {
setLoginError("Invalid email or password");
toast.error("Invalid email or password");
}
return;
@ -60,6 +64,7 @@ console.log('result', result);
router.refresh();
} catch (error) {
console.error("Login error:", error);
setLoginError("An error occurred during login");
toast.error("An error occurred during login");
} finally {
setIsLoading(false);
@ -70,12 +75,13 @@ console.log('result', result);
useEffect(() => {
const error = searchParams.get("error");
if (error) {
setLoginError(decodeURIComponent(error));
toast.error(decodeURIComponent(error));
}
}, [searchParams]);
return (
<div className="flex min-h-screen items-center justify-center bg-slate-800 px-4 py-12">
<div className="flex min-h-screen items-center justify-center bg-[#1a2840] px-4 py-12">
<div className="w-full max-w-md space-y-6">
<div className="text-center space-y-2">
<h1 className="text-2xl font-bold tracking-tight text-white">
@ -86,8 +92,8 @@ console.log('result', result);
</p>
</div>
<div className="bg-slate-900 rounded-xl p-6 shadow-lg">
<Alert variant="info" className="mb-6 bg-slate-800 border-blue-500">
<div className="bg-[#192133] rounded-xl p-6 shadow-lg border border-slate-800">
<Alert variant="info" className="mb-6 bg-[#192133] border-blue-500/30">
<Info className="h-4 w-4 text-blue-500" />
<AlertTitle className="text-blue-500">Demo Account</AlertTitle>
<AlertDescription className="text-slate-300">
@ -96,6 +102,16 @@ console.log('result', result);
</AlertDescription>
</Alert>
{loginError && (
<Alert variant="destructive" className="mb-4 bg-red-900/30 border-red-500/50">
<AlertCircle className="h-4 w-4 text-red-500" />
<AlertTitle className="text-red-400">Login Failed</AlertTitle>
<AlertDescription className="text-slate-300">
{loginError}
</AlertDescription>
</Alert>
)}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
@ -125,7 +141,7 @@ console.log('result', result);
<FormLabel className="text-slate-300">Password</FormLabel>
<FormControl>
<Input
type="text"
type="password"
placeholder="••••••••"
autoComplete="current-password"
{...field}

View File

@ -5,7 +5,7 @@ export interface IUser extends Document {
name: string;
email: string;
password: string;
role: 'admin' | 'hr';
role: 'admin' | 'hr' | 'user';
isActive: boolean;
createdAt: Date;
updatedAt: Date;
@ -31,7 +31,7 @@ const UserSchema = new Schema<IUser>({
},
role: {
type: String,
enum: ['admin', 'hr'],
enum: ['admin', 'hr', 'user'],
default: 'hr',
},
isActive: {

24
test-db.js Normal file
View File

@ -0,0 +1,24 @@
// Test database connection
require('dotenv').config({ path: '.env.local' });
const mongoose = require('mongoose');
async function testDbConnection() {
try {
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/candidate_filter_portal';
console.log('Connecting to:', mongoUri);
await mongoose.connect(mongoUri, {
serverSelectionTimeoutMS: 5000
});
console.log('MongoDB connected successfully!');
// Close the connection
await mongoose.disconnect();
console.log('MongoDB disconnected');
} catch (error) {
console.error('MongoDB connection error:', error);
}
}
testDbConnection();

36
test-login-flow.js Normal file
View File

@ -0,0 +1,36 @@
// Test login flow script
const credentials = {
admin: {
email: "admin@example.com",
password: "password123"
},
hr: {
email: "hr@example.com",
password: "password123"
}
};
// Simulate login with demo users
function testLoginFlow() {
console.log("Testing login flow with demo credentials");
// Admin user test
console.log("\nTesting admin login:");
console.log("Email:", credentials.admin.email);
console.log("Password:", credentials.admin.password);
console.log("Expected result: Success - Should authenticate and return admin role");
// HR user test
console.log("\nTesting HR login:");
console.log("Email:", credentials.hr.email);
console.log("Password:", credentials.hr.password);
console.log("Expected result: Success - Should authenticate and return hr role");
console.log("\nTo test this login:");
console.log("1. Go to the login page");
console.log("2. Enter the credentials above");
console.log("3. Click 'Sign In'");
console.log("4. You should be redirected to the dashboard");
}
testLoginFlow();

17
test-login.js Normal file
View File

@ -0,0 +1,17 @@
// Test login script
const bcrypt = require('bcryptjs');
// Test password hashing to verify what's stored in the demo users
async function testPassword() {
// Hash password
const password = 'password123';
const hashedPassword = await bcrypt.hash(password, 12);
console.log('Hashed password:', hashedPassword);
// Compare with stored hash
const storedHash = '$2b$12$CCIPxtBFUjfJqz8sMvJdj.9MQLE6ghAoIl/amO9uNaRc6VXcWUDiW';
const isMatch = await bcrypt.compare(password, storedHash);
console.log('Password matches:', isMatch);
}
testPassword();