1. Passwords Hash කිරීම සහ bcrypt
පරිශීලකයින්ගේ passwords කිසිවිටකවත් එලෙසම database එකේ ගබඩා කරන්නේ නැත. එය ඉතාමත් අනාරක්ෂිතයි. ඒ වෙනුවට, අපි password එක සංකීර්ණ, ආපසු හැරවිය නොහැකි අකුරු සහ ඉලක්කම් එකතුවක් (a "hash") බවට පත් කර database එකේ ගබඩා කරමු. මේ සඳහා bcrypt නමැති ජනප්රිය library එක භාවිතා කරමු.
පළමුව, terminal එකේ පහත command එක run කර bcrypt library එක install කරගන්න.
2. Signup (ලියාපදිංචි වීමේ) ක්රියාවලිය සෑදීම
Signup ක්රියාවලියට කොටස් දෙකක් ඇත: Backend API Route එක (දත්ත ලබාගෙන database එකට ඇතුළත් කරන) සහ Frontend Form එක (පරිශීලකයාට දත්ත ඇතුළත් කිරීමට).
➡️ Signup API Route (Backend)
Next.js වලදී, app directory එක තුළ api නමින් folder එකක් සාදා, ඒ තුළ අපේ backend logic එක ලිවිය හැක. app/api/auth/register/route.ts යන path එකට අලුත් file එකක් සාදන්න.
දැන්, එම route.ts file එකට පහත කේතය ඇතුළත් කරන්න.
import { NextResponse } from 'next/server';
import db from '@/lib/db'; // Assuming db is in lib folder
import bcrypt from 'bcrypt';
export async function POST(request: Request) {
try {
const { username, password } = await request.json();
// Check if user already exists
const existingUser = db.prepare('SELECT * FROM users WHERE username = ?').get(username);
if (existingUser) {
return NextResponse.json({ error: 'Username already taken' }, { status: 409 });
}
// Hash the password
const hashedPassword = await bcrypt.hash(password, 10); // 10 is the salt rounds
// Insert user into the database
const stmt = db.prepare('INSERT INTO users (username, password_hash) VALUES (?, ?)');
stmt.run(username, hashedPassword);
return NextResponse.json({ message: 'User created successfully' }, { status: 201 });
} catch (error) {
console.error(error);
return NextResponse.json({ error: 'An error occurred' }, { status: 500 });
}
}
කේතය පැහැදිලි කිරීම:
request.json(): Frontend එකෙන් එවන username සහ password ලබාගනී.db.prepare(...): Database එකට SQL query එකක් යැවීමට සූදානම් කරයි.bcrypt.hash(password, 10): ලබාගත් password එක hash කරයි.stmt.run(...): Hash කළ password එක සමඟ නව user ව database එකට ඇතුළත් කරයි.
➡️ Signup Form (Frontend)
දැන් app/auth/register/page.tsx යන path එකට file එකක් සාදා, user ට register වීමට අවශ්ය form එක නිර්මාණය කරමු. මෙය user සමග interact කරන නිසා, "use client" ලෙස සඳහන් කළ යුතුය.
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function RegisterPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (res.ok) {
router.push('/auth/login'); // Redirect to login page on success
} else {
const data = await res.json();
setError(data.error || 'Registration failed.');
}
};
return (
<div className="row justify-content-center">
<div className="col-md-6">
<h1>Register</h1>
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label className="form-label">Username</label>
<input
type="text"
className="form-control"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<input
type="password"
className="form-control"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="text-danger">{error}</p>}
<button type="submit" className="btn btn-primary">Register</button>
</form>
</div>
</div>
);
}
3. Login (ඇතුල් වීමේ) ක්රියාවලිය සෑදීම
Login ක්රියාවලියද signup මෙන්ම backend API route එකක් සහ frontend form එකකින් සමන්විත වේ.
➡️ Login API Route (Backend)
app/api/auth/login/route.ts යන path එකට අලුත් file එකක් සාදා පහත කේතය ඇතුළත් කරන්න.
import { NextResponse } from 'next/server';
import db from '@/lib/db';
import bcrypt from 'bcrypt';
export async function POST(request: Request) {
try {
const { username, password } = await request.json();
// Find the user by username
const user = db.prepare('SELECT * FROM users WHERE username = ?').get(username) as any;
if (!user) {
return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });
}
// Compare the provided password with the stored hash
const isValid = await bcrypt.compare(password, user.password_hash);
if (!isValid) {
return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });
}
// --- IMPORTANT ---
// In the next module, we will create a session (e.g., using JWT) here.
// For now, we just return a success message.
return NextResponse.json({ message: 'Logged in successfully' }, { status: 200 });
} catch (error) {
console.error(error);
return NextResponse.json({ error: 'An error occurred' }, { status: 500 });
}
}
කේතය පැහැදිලි කිරීම:
- පළමුව, username එක මගින් user ව database එකෙන් සොයා ගනී.
bcrypt.compare()function එක මගින් user ඇතුළත් කළ password එකත්, database එකේ ඇති hash කළ password එකත් සසඳා බලයි.- Passwords දෙකම ගැලපේ නම්, login වීම සාර්ථක වේ.
➡️ Login Form (Frontend)
දැන් app/auth/login/page.tsx යන path එකට file එකක් සාදා login form එක නිර්මාණය කරමු.
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
const handleSubmit = 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 }),
});
if (res.ok) {
router.push('/notes'); // Redirect to notes dashboard on success
} else {
const data = await res.json();
setError(data.error || 'Login failed.');
}
};
return (
<div className="row justify-content-center">
<div className="col-md-6">
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label className="form-label">Username</label>
<input
type="text"
className="form-control"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<input
type="password"
className="form-control"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="text-danger">{error}</p>}
<button type="submit" className="btn btn-primary">Login</button>
</form>
</div>
</div>
);
}