const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const db = require('../db/db'); const { BCRYPT_ROUNDS } = require('../config'); const { stripTags } = require('../utils/sanitize'); function signToken(user) { return jwt.sign( { id: user.id, email: user.email, role: user.role, name: user.name, tv: user.token_version || 0 }, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: process.env.JWT_EXPIRES_IN || '7d' } ); } async function register(req, res, next) { try { const { password, name } = req.body; const email = req.body.email?.trim().toLowerCase(); if (!email || !password || !name) return res.status(400).json({ error: 'email, password and name are required' }); if (password.length < 6) return res.status(400).json({ error: 'Password must be at least 6 characters' }); if (db.prepare('SELECT id FROM users WHERE email = ?').get(email)) return res.status(409).json({ error: 'Email already registered' }); const cleanName = stripTags(name.trim()); const hash = await bcrypt.hash(password, BCRYPT_ROUNDS); const { lastInsertRowid } = db.prepare( 'INSERT INTO users (email, password_hash, name) VALUES (?, ?, ?)' ).run(email, hash, cleanName); const user = db.prepare('SELECT id, email, name, role, token_version FROM users WHERE id = ?').get(lastInsertRowid); const token = signToken(user); res.status(201).json({ token, user }); } catch (err) { next(err); } } async function login(req, res, next) { try { const { password } = req.body; const email = req.body.email?.trim().toLowerCase(); if (!email || !password) return res.status(400).json({ error: 'email and password are required' }); const user = db.prepare( 'SELECT id, email, name, role, password_hash, token_version FROM users WHERE email = ?' ).get(email); if (!user || !(await bcrypt.compare(password, user.password_hash))) return res.status(401).json({ error: 'Invalid credentials' }); db.prepare("UPDATE users SET last_login = datetime('now') WHERE id = ?").run(user.id); const token = signToken(user); res.json({ token, user: { id: user.id, email: user.email, name: user.name, role: user.role } }); } catch (err) { next(err); } } function me(req, res) { const user = db.prepare( 'SELECT id, email, name, role, created_at, last_login FROM users WHERE id = ?' ).get(req.user.id); res.json(user); } async function updateProfile(req, res, next) { try { const { name, currentPassword, newPassword } = req.body; const user = db.prepare('SELECT id, name, email, role, password_hash FROM users WHERE id = ?').get(req.user.id); if (name?.trim() && name.trim() !== user.name) { db.prepare('UPDATE users SET name = ? WHERE id = ?').run(stripTags(name.trim()), user.id); } if (newPassword) { if (!currentPassword) return res.status(400).json({ error: 'Текущий пароль обязателен' }); const valid = await bcrypt.compare(currentPassword, user.password_hash); if (!valid) return res.status(401).json({ error: 'Неверный текущий пароль' }); if (newPassword.length < 6) return res.status(400).json({ error: 'Пароль минимум 6 символов' }); const hash = await bcrypt.hash(newPassword, BCRYPT_ROUNDS); db.prepare('UPDATE users SET password_hash = ?, token_version = token_version + 1 WHERE id = ?').run(hash, user.id); } const updated = db.prepare('SELECT id, email, name, role, created_at, token_version FROM users WHERE id = ?').get(user.id); const token = signToken(updated); res.json({ user: updated, token }); } catch (err) { next(err); } } module.exports = { register, login, me, updateProfile };