Files
Final-Year-Project/Backend/routes/auth.ts

135 lines
3.0 KiB
TypeScript

import { Router } from 'express';
import { and, eq } from 'drizzle-orm';
import { z } from 'zod';
import { db } from '../db/client';
import { users } from '../db/schema';
import { requireAuth } from '../middleware/auth';
import { signAccessToken } from '../utils/jwt';
import { hashPassword, verifyPassword } from '../utils/password';
const router = Router();
const registerSchema = z.object({
email: z.email().trim().toLowerCase(),
password: z.string().min(8),
name: z.string().trim().min(1).max(255),
});
const loginSchema = z.object({
email: z.email().trim().toLowerCase(),
password: z.string().min(1),
});
router.post('/register', async (req, res) => {
const parsed = registerSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: 'Invalid request body', errors: parsed.error.flatten() });
return;
}
const { email, password, name } = parsed.data;
const existingUser = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (existingUser) {
res.status(409).json({ message: 'Email already in use' });
return;
}
const passwordHash = await hashPassword(password);
const [newUser] = await db
.insert(users)
.values({
email,
name,
passwordHash,
})
.returning({
id: users.id,
email: users.email,
name: users.name,
createdAt: users.createdAt,
});
if (!newUser) {
res.status(500).json({ message: 'Failed to create user' });
return;
}
const token = signAccessToken({ userId: newUser.id, email: newUser.email });
res.status(201).json({ token, user: newUser });
});
router.post('/login', async (req, res) => {
const parsed = loginSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: 'Invalid request body', errors: parsed.error.flatten() });
return;
}
const { email, password } = parsed.data;
const user = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (!user) {
res.status(401).json({ message: 'Invalid email or password' });
return;
}
const isPasswordValid = await verifyPassword(password, user.passwordHash);
if (!isPasswordValid) {
res.status(401).json({ message: 'Invalid email or password' });
return;
}
const token = signAccessToken({ userId: user.id, email: user.email });
res.json({
token,
user: {
id: user.id,
email: user.email,
name: user.name,
createdAt: user.createdAt,
},
});
});
router.get('/me', requireAuth, async (req, res) => {
const authenticatedUser = req.user;
if (!authenticatedUser) {
res.status(401).json({ message: 'Unauthorized' });
return;
}
const user = await db.query.users.findFirst({
where: and(eq(users.id, authenticatedUser.userId), eq(users.email, authenticatedUser.email)),
columns: {
id: true,
email: true,
name: true,
createdAt: true,
},
});
if (!user) {
res.status(404).json({ message: 'User not found' });
return;
}
res.json({ user });
});
export default router;