|
|
const jwt = require('jsonwebtoken');
|
|
|
const User = require('../models/User');
|
|
|
const Joi = require('joi');
|
|
|
|
|
|
|
|
|
const registerSchema = Joi.object({
|
|
|
username: Joi.string().alphanum().min(3).max(30).required(),
|
|
|
email: Joi.string().email().required(),
|
|
|
password: Joi.string().min(6).required()
|
|
|
});
|
|
|
|
|
|
const loginSchema = Joi.object({
|
|
|
email: Joi.string().email().required(),
|
|
|
password: Joi.string().min(6).required()
|
|
|
});
|
|
|
|
|
|
|
|
|
const generateToken = (userId) => {
|
|
|
return jwt.sign({ userId }, process.env.JWT_SECRET, {
|
|
|
expiresIn: process.env.JWT_EXPIRE || '7d'
|
|
|
});
|
|
|
};
|
|
|
|
|
|
|
|
|
exports.register = async (req, res) => {
|
|
|
try {
|
|
|
|
|
|
const { error } = registerSchema.validate(req.body);
|
|
|
if (error) {
|
|
|
return res.status(400).json({ error: error.details[0].message });
|
|
|
}
|
|
|
|
|
|
const { username, email, password } = req.body;
|
|
|
|
|
|
|
|
|
const existingUser = await User.findOne({
|
|
|
$or: [{ email }, { username }]
|
|
|
});
|
|
|
|
|
|
if (existingUser) {
|
|
|
return res.status(400).json({
|
|
|
error: existingUser.email === email ? 'Email already registered' : 'Username already taken'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
const user = new User({ username, email, password });
|
|
|
await user.save();
|
|
|
|
|
|
|
|
|
const token = generateToken(user._id);
|
|
|
|
|
|
res.status(201).json({
|
|
|
message: 'User registered successfully',
|
|
|
token,
|
|
|
user
|
|
|
});
|
|
|
} catch (error) {
|
|
|
console.error('Registration error:', error);
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
exports.login = async (req, res) => {
|
|
|
try {
|
|
|
|
|
|
const { error } = loginSchema.validate(req.body);
|
|
|
if (error) {
|
|
|
return res.status(400).json({ error: error.details[0].message });
|
|
|
}
|
|
|
|
|
|
const { email, password } = req.body;
|
|
|
|
|
|
|
|
|
const user = await User.findOne({ email }).select('+password');
|
|
|
if (!user) {
|
|
|
return res.status(401).json({ error: 'Invalid credentials' });
|
|
|
}
|
|
|
|
|
|
|
|
|
const isPasswordValid = await user.comparePassword(password);
|
|
|
if (!isPasswordValid) {
|
|
|
return res.status(401).json({ error: 'Invalid credentials' });
|
|
|
}
|
|
|
|
|
|
|
|
|
user.lastSeen = new Date();
|
|
|
await user.save();
|
|
|
|
|
|
|
|
|
const token = generateToken(user._id);
|
|
|
|
|
|
res.json({
|
|
|
message: 'Login successful',
|
|
|
token,
|
|
|
user
|
|
|
});
|
|
|
} catch (error) {
|
|
|
console.error('Login error:', error);
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
exports.getMe = async (req, res) => {
|
|
|
try {
|
|
|
const user = await User.findById(req.userId);
|
|
|
if (!user) {
|
|
|
return res.status(404).json({ error: 'User not found' });
|
|
|
}
|
|
|
|
|
|
res.json({ user });
|
|
|
} catch (error) {
|
|
|
console.error('Get user error:', error);
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
exports.updateProfile = async (req, res) => {
|
|
|
try {
|
|
|
const { username, avatar } = req.body;
|
|
|
const updates = {};
|
|
|
|
|
|
if (username) {
|
|
|
|
|
|
const existingUser = await User.findOne({
|
|
|
username,
|
|
|
_id: { $ne: req.userId }
|
|
|
});
|
|
|
if (existingUser) {
|
|
|
return res.status(400).json({ error: 'Username already taken' });
|
|
|
}
|
|
|
updates.username = username;
|
|
|
}
|
|
|
|
|
|
if (avatar) updates.avatar = avatar;
|
|
|
|
|
|
const user = await User.findByIdAndUpdate(
|
|
|
req.userId,
|
|
|
updates,
|
|
|
{ new: true, runValidators: true }
|
|
|
);
|
|
|
|
|
|
res.json({
|
|
|
message: 'Profile updated successfully',
|
|
|
user
|
|
|
});
|
|
|
} catch (error) {
|
|
|
console.error('Update profile error:', error);
|
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
|
}
|
|
|
}; |