Moved register and login to user folder. Setup basic login, register and JWT
This commit is contained in:
7
src/app/api/auth/login/route.ts
Normal file
7
src/app/api/auth/login/route.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { login } from '../../../../../src/controllers/authController'; // Import the login function from authController
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
// Delegate the request to the login function in the controller
|
||||
return login(req);
|
||||
}
|
||||
63
src/app/api/auth/register/route.ts
Normal file
63
src/app/api/auth/register/route.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import bcrypt from 'bcrypt';
|
||||
import prisma from '../../../models/prismaClient';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
console.log('Request Body:', body); // Log the request body
|
||||
|
||||
const { name, username, email, password, role, company, address, phone, privatePhone, cvr } = body;
|
||||
|
||||
// Check if the username already exists
|
||||
const existingUserByUsername = await prisma.user.findUnique({
|
||||
where: { username },
|
||||
});
|
||||
if (existingUserByUsername) {
|
||||
console.log('Username already exists');
|
||||
return NextResponse.json({ error: 'Username already exists' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Check if the email already exists
|
||||
const existingUserByEmail = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
});
|
||||
if (existingUserByEmail) {
|
||||
console.log('Email already exists');
|
||||
return NextResponse.json({ error: 'Email already exists' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Hash the password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create the user
|
||||
const newUser = await prisma.user.create({
|
||||
data: {
|
||||
name,
|
||||
username,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role,
|
||||
address,
|
||||
phone,
|
||||
company: role === 'BUSINESS' ? company : null,
|
||||
privatePhone: role === 'BUSINESS' ? privatePhone : null,
|
||||
cvr: role === 'BUSINESS' ? cvr : null,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('User created successfully:', newUser);
|
||||
return NextResponse.json({ user: newUser }, { status: 201 });
|
||||
|
||||
} catch (error) {
|
||||
// Check if the error is an instance of Error and log the details
|
||||
if (error instanceof Error) {
|
||||
console.error('Error message:', error.message);
|
||||
console.error('Full error stack:', error.stack);
|
||||
} else {
|
||||
console.error('Unknown error:', error); // Log if the error is not of type `Error`
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,11 @@ const Header: React.FC = () => {
|
||||
Contact
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/account" className='hover:text-gray-400'>
|
||||
Account
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
5
src/app/models/prismaClient.ts
Normal file
5
src/app/models/prismaClient.ts
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
export default prisma;
|
||||
91
src/app/user/login/page.tsx
Normal file
91
src/app/user/login/page.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const LoginPage = () => {
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const router = useRouter();
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
const res = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
|
||||
// Check if the response has a body to parse
|
||||
let data = null;
|
||||
try {
|
||||
data = await res.json();
|
||||
} catch (error) {
|
||||
// If the response body is empty or not valid JSON, handle it gracefully
|
||||
console.error('Error parsing response JSON:', error);
|
||||
}
|
||||
|
||||
// Handle successful login
|
||||
if (res.ok && data?.token) {
|
||||
localStorage.setItem('token', data.token); // Store JWT token in localStorage
|
||||
router.push('/'); // Redirect to a protected page (e.g., dashboard)
|
||||
} else {
|
||||
// Handle login errors, ensuring data is valid
|
||||
setError(data?.error || 'Failed to log in');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center h-screen bg-gray-100">
|
||||
<div className="bg-white p-6 rounded-lg shadow-md w-96">
|
||||
<h2 className="text-2xl font-bold mb-4">Login</h2>
|
||||
|
||||
{error && <p className="text-red-500 mb-4">{error}</p>}
|
||||
|
||||
<form onSubmit={handleLogin}>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="username" className="block text-sm font-medium text-gray-700">
|
||||
Username
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
224
src/app/user/register/page.tsx
Normal file
224
src/app/user/register/page.tsx
Normal file
@ -0,0 +1,224 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
|
||||
const RegisterPage = () => {
|
||||
const [role, setRole] = useState<'PRIVATE' | 'BUSINESS' | null>(null); // Initially, no role is selected
|
||||
const [name, setName] = useState('');
|
||||
const [company, setCompany] = useState('');
|
||||
const [address, setAddress] = useState('');
|
||||
const [phone, setPhone] = useState('');
|
||||
const [privatePhone, setPrivatePhone] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [cvr, setCvr] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [username, setUsername] = useState('');
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const userData: any = {
|
||||
name,
|
||||
address,
|
||||
phone,
|
||||
email,
|
||||
password,
|
||||
username,
|
||||
role,
|
||||
};
|
||||
|
||||
if (role === 'BUSINESS') {
|
||||
userData.company = company;
|
||||
userData.privatePhone = privatePhone;
|
||||
userData.cvr = cvr;
|
||||
}
|
||||
|
||||
console.log('Submitting user data:', userData); // Log form data before submission
|
||||
|
||||
const res = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(userData),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
router.push('/user/login');
|
||||
} else {
|
||||
const errorData = await res.json();
|
||||
console.error('Failed to register:', errorData); // Log the error response from the server
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center items-center h-screen bg-gray-100">
|
||||
{/* Registration Header */}
|
||||
<div className="mb-8 text-center">
|
||||
<h2 className="text-3xl font-bold mb-4">Register Here</h2>
|
||||
<p className="text-gray-600">Please select whether you're a Private or Business user to register.</p>
|
||||
</div>
|
||||
|
||||
{/* Role Selection */}
|
||||
{role === null && (
|
||||
<div className="flex space-x-4 mb-6">
|
||||
<button
|
||||
className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-6 rounded focus:outline-none focus:shadow-outline"
|
||||
onClick={() => setRole('PRIVATE')}
|
||||
>
|
||||
Private
|
||||
</button>
|
||||
<button
|
||||
className="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-6 rounded focus:outline-none focus:shadow-outline"
|
||||
onClick={() => setRole('BUSINESS')}
|
||||
>
|
||||
Business
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Registration Form */}
|
||||
{role && (
|
||||
<div className="bg-white p-8 rounded-lg shadow-md w-96">
|
||||
<h3 className="text-2xl font-bold mb-4">{role === 'PRIVATE' ? 'Private Registration' : 'Business Registration'}</h3>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="name" className="block text-sm font-medium text-gray-700">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{role === 'BUSINESS' && (
|
||||
<>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="company" className="block text-sm font-medium text-gray-700">Company</label>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
value={company}
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required={role === 'BUSINESS'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="privatePhone" className="block text-sm font-medium text-gray-700">Private Phone</label>
|
||||
<input
|
||||
type="text"
|
||||
id="privatePhone"
|
||||
value={privatePhone}
|
||||
onChange={(e) => setPrivatePhone(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required={role === 'BUSINESS'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="cvr" className="block text-sm font-medium text-gray-700">CVR</label>
|
||||
<input
|
||||
type="text"
|
||||
id="cvr"
|
||||
value={cvr}
|
||||
onChange={(e) => setCvr(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required={role === 'BUSINESS'}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="address" className="block text-sm font-medium text-gray-700">Address</label>
|
||||
<input
|
||||
type="text"
|
||||
id="address"
|
||||
value={address}
|
||||
onChange={(e) => setAddress(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">Phone</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="username" className="block text-sm font-medium text-gray-700">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="password" className="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||
>
|
||||
Register
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Already Registered */}
|
||||
<div className="mt-6">
|
||||
<p className="text-gray-600">
|
||||
Already registered?{' '}
|
||||
<Link href="/user/login" className="text-blue-500 hover:underline">
|
||||
Login here
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegisterPage;
|
||||
Reference in New Issue
Block a user