| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- import { Router, Request, Response } from 'express';
- import bcrypt from 'bcryptjs';
- import jwt from 'jsonwebtoken';
- import { prisma } from '../lib/prisma';
- import { authMiddleware } from '../lib/auth';
- const router = Router();
- const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret';
- const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
- // POST /api/auth/register
- router.post('/register', async (req: Request, res: Response) => {
- try {
- const { email, name, password } = req.body;
- if (!email || !name || !password) {
- res.status(400).json({ error: 'email, name, and password are required' });
- return;
- }
- if (password.length < 6) {
- res.status(400).json({ error: 'Password must be at least 6 characters' });
- return;
- }
- const existing = await prisma.user.findUnique({ where: { email } });
- if (existing) {
- res.status(409).json({ error: 'Email already registered' });
- return;
- }
- const hashed = await bcrypt.hash(password, 12);
- const user = await prisma.user.create({
- data: { email, name, password: hashed },
- select: { id: true, email: true, name: true, role: true, avatarUrl: true },
- });
- const token = jwt.sign(
- { userId: user.id, email: user.email, role: user.role },
- JWT_SECRET,
- { expiresIn: JWT_EXPIRES_IN } as jwt.SignOptions
- );
- res.cookie('token', token, {
- httpOnly: true,
- secure: false, // set true if serving over HTTPS
- sameSite: 'lax',
- maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
- });
- res.status(201).json({ user, token });
- } catch (err) {
- console.error('Register error:', err);
- res.status(500).json({ error: 'Internal server error' });
- }
- });
- // POST /api/auth/login
- router.post('/login', async (req: Request, res: Response) => {
- try {
- const { email, password } = req.body;
- if (!email || !password) {
- res.status(400).json({ error: 'email and password are required' });
- return;
- }
- const user = await prisma.user.findUnique({ where: { email } });
- if (!user) {
- res.status(401).json({ error: 'Invalid credentials' });
- return;
- }
- const valid = await bcrypt.compare(password, user.password);
- if (!valid) {
- res.status(401).json({ error: 'Invalid credentials' });
- return;
- }
- const token = jwt.sign(
- { userId: user.id, email: user.email, role: user.role },
- JWT_SECRET,
- { expiresIn: JWT_EXPIRES_IN } as jwt.SignOptions
- );
- res.cookie('token', token, {
- httpOnly: true,
- secure: false, // set true if serving over HTTPS
- sameSite: 'lax',
- maxAge: 7 * 24 * 60 * 60 * 1000,
- });
- res.json({
- user: {
- id: user.id,
- email: user.email,
- name: user.name,
- role: user.role,
- avatarUrl: user.avatarUrl,
- },
- token,
- });
- } catch (err) {
- console.error('Login error:', err);
- res.status(500).json({ error: 'Internal server error' });
- }
- });
- // POST /api/auth/logout
- router.post('/logout', (_req: Request, res: Response) => {
- res.clearCookie('token');
- res.json({ message: 'Logged out' });
- });
- // GET /api/auth/me
- router.get('/me', authMiddleware, async (req: Request, res: Response) => {
- try {
- const user = await prisma.user.findUnique({
- where: { id: req.user!.userId },
- select: { id: true, email: true, name: true, role: true, avatarUrl: true },
- });
- if (!user) {
- res.status(404).json({ error: 'User not found' });
- return;
- }
- res.json({ user });
- } catch (err) {
- console.error('Me error:', err);
- res.status(500).json({ error: 'Internal server error' });
- }
- });
- export default router;
|