246 lines
6.6 KiB
JavaScript
246 lines
6.6 KiB
JavaScript
import express from 'express';
|
|
import bcrypt from 'bcrypt';
|
|
import jwt from 'jsonwebtoken';
|
|
import db from '../database.js';
|
|
import { authenticateToken } from '../middleware/auth.js';
|
|
|
|
const router = express.Router();
|
|
|
|
// Register new user
|
|
router.post('/register', async (req, res) => {
|
|
const { username, email, password } = req.body;
|
|
|
|
if (!username || !email || !password) {
|
|
return res.status(400).json({ error: 'Username, email, and password are required' });
|
|
}
|
|
|
|
if (password.length < 6) {
|
|
return res.status(400).json({ error: 'Password must be at least 6 characters long' });
|
|
}
|
|
|
|
try {
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
const query = 'INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)';
|
|
|
|
db.run(query, [username, email, hashedPassword], function (err) {
|
|
if (err) {
|
|
if (err.message.includes('UNIQUE')) {
|
|
return res.status(400).json({ error: 'Username or email already exists' });
|
|
}
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
const token = jwt.sign(
|
|
{ id: this.lastID, username, email },
|
|
process.env.JWT_SECRET,
|
|
{ expiresIn: '7d' }
|
|
);
|
|
|
|
res.status(201).json({
|
|
message: 'User registered successfully',
|
|
token,
|
|
user: {
|
|
id: this.lastID,
|
|
username,
|
|
email
|
|
}
|
|
});
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: 'Error hashing password' });
|
|
}
|
|
});
|
|
|
|
// Login
|
|
router.post('/login', (req, res) => {
|
|
const { username, password } = req.body;
|
|
|
|
if (!username || !password) {
|
|
return res.status(400).json({ error: 'Username and password are required' });
|
|
}
|
|
|
|
const query = 'SELECT * FROM users WHERE username = ?';
|
|
|
|
db.get(query, [username], async (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
if (!user) {
|
|
return res.status(401).json({ error: 'Invalid username or password' });
|
|
}
|
|
|
|
try {
|
|
const validPassword = await bcrypt.compare(password, user.password_hash);
|
|
|
|
if (!validPassword) {
|
|
return res.status(401).json({ error: 'Invalid username or password' });
|
|
}
|
|
|
|
const token = jwt.sign(
|
|
{ id: user.id, username: user.username, email: user.email },
|
|
process.env.JWT_SECRET,
|
|
{ expiresIn: '7d' }
|
|
);
|
|
|
|
res.json({
|
|
message: 'Login successful',
|
|
token,
|
|
user: {
|
|
id: user.id,
|
|
username: user.username,
|
|
email: user.email
|
|
}
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: 'Error verifying password' });
|
|
}
|
|
});
|
|
});
|
|
|
|
// Get current user
|
|
router.get('/me', authenticateToken, (req, res) => {
|
|
const query = 'SELECT id, username, email, theme, created_at FROM users WHERE id = ?';
|
|
|
|
db.get(query, [req.user.id], (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found' });
|
|
}
|
|
|
|
res.json({ user });
|
|
});
|
|
});
|
|
|
|
// Update user theme preference
|
|
router.put('/me/theme', authenticateToken, (req, res) => {
|
|
const { theme } = req.body;
|
|
|
|
if (!['light', 'dark'].includes(theme)) {
|
|
return res.status(400).json({ error: 'Theme must be "light" or "dark"' });
|
|
}
|
|
|
|
const query = 'UPDATE users SET theme = ? WHERE id = ?';
|
|
|
|
db.run(query, [theme, req.user.id], (err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
res.json({ message: 'Theme updated', theme });
|
|
});
|
|
});
|
|
|
|
// Update user email
|
|
router.put('/me/email', authenticateToken, async (req, res) => {
|
|
const { newEmail, password } = req.body;
|
|
|
|
if (!newEmail || !password) {
|
|
return res.status(400).json({ error: 'New email and password are required' });
|
|
}
|
|
|
|
try {
|
|
// Get current user
|
|
const userQuery = 'SELECT * FROM users WHERE id = ?';
|
|
|
|
db.get(userQuery, [req.user.id], async (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found' });
|
|
}
|
|
|
|
// Verify password
|
|
const validPassword = await bcrypt.compare(password, user.password_hash);
|
|
|
|
if (!validPassword) {
|
|
return res.status(401).json({ error: 'Invalid password' });
|
|
}
|
|
|
|
// Check if email already exists
|
|
const emailCheckQuery = 'SELECT id FROM users WHERE email = ? AND id != ?';
|
|
|
|
db.get(emailCheckQuery, [newEmail, req.user.id], (err, existingUser) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
if (existingUser) {
|
|
return res.status(400).json({ error: 'Email already in use' });
|
|
}
|
|
|
|
// Update email
|
|
const updateQuery = 'UPDATE users SET email = ? WHERE id = ?';
|
|
|
|
db.run(updateQuery, [newEmail, req.user.id], (err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
res.json({ message: 'Email updated successfully', email: newEmail });
|
|
});
|
|
});
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: 'Error updating email' });
|
|
}
|
|
});
|
|
|
|
// Update user password
|
|
router.put('/me/password', authenticateToken, async (req, res) => {
|
|
const { currentPassword, newPassword } = req.body;
|
|
|
|
if (!currentPassword || !newPassword) {
|
|
return res.status(400).json({ error: 'Current and new password are required' });
|
|
}
|
|
|
|
if (newPassword.length < 6) {
|
|
return res.status(400).json({ error: 'New password must be at least 6 characters long' });
|
|
}
|
|
|
|
try {
|
|
// Get current user
|
|
const userQuery = 'SELECT * FROM users WHERE id = ?';
|
|
|
|
db.get(userQuery, [req.user.id], async (err, user) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found' });
|
|
}
|
|
|
|
// Verify current password
|
|
const validPassword = await bcrypt.compare(currentPassword, user.password_hash);
|
|
|
|
if (!validPassword) {
|
|
return res.status(401).json({ error: 'Invalid current password' });
|
|
}
|
|
|
|
// Hash new password
|
|
const hashedPassword = await bcrypt.hash(newPassword, 10);
|
|
|
|
// Update password
|
|
const updateQuery = 'UPDATE users SET password_hash = ? WHERE id = ?';
|
|
|
|
db.run(updateQuery, [hashedPassword, req.user.id], (err) => {
|
|
if (err) {
|
|
return res.status(500).json({ error: err.message });
|
|
}
|
|
|
|
res.json({ message: 'Password updated successfully' });
|
|
});
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: 'Error updating password' });
|
|
}
|
|
});
|
|
|
|
export default router;
|