Files
neomovies-api/src/routes/auth.js
2025-07-07 20:39:52 +03:00

208 lines
6.0 KiB
JavaScript

const { Router } = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
const { getDb } = require('../db');
const { sendVerificationEmail } = require('../utils/mailer');
/**
* @swagger
* tags:
* name: auth
* description: Операции авторизации
*/
const router = Router();
// Helper to generate 6-digit code
function generateCode() {
return Math.floor(100000 + Math.random() * 900000).toString();
}
// Register
/**
* @swagger
* /auth/register:
* post:
* tags: [auth]
* summary: Регистрация пользователя
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* email:
* type: string
* password:
* type: string
* name:
* type: string
* responses:
* 200:
* description: OK
*/
router.post('/register', async (req, res) => {
try {
const { email, password, name } = req.body;
if (!email || !password) return res.status(400).json({ error: 'Email and password required' });
const db = await getDb();
const existing = await db.collection('users').findOne({ email });
if (existing) return res.status(400).json({ error: 'Email already registered' });
const hashed = await bcrypt.hash(password, 12);
const code = generateCode();
const codeExpires = new Date(Date.now() + 10 * 60 * 1000);
await db.collection('users').insertOne({
email,
password: hashed,
name: name || email,
verified: false,
verificationCode: code,
verificationExpires: codeExpires,
isAdmin: false,
adminVerified: false,
createdAt: new Date()
});
await sendVerificationEmail(email, code);
res.json({ success: true, message: 'Registered. Check email for code.' });
} catch (err) {
console.error('Register error:', err);
res.status(500).json({ error: 'Registration failed' });
}
});
// Verify email
/**
* @swagger
* /auth/verify:
* post:
* tags: [auth]
* summary: Подтверждение email
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* email:
* type: string
* code:
* type: string
* responses:
* 200:
* description: OK
*/
router.post('/verify', async (req, res) => {
try {
const { email, code } = req.body;
const db = await getDb();
const user = await db.collection('users').findOne({ email });
if (!user) return res.status(400).json({ error: 'User not found' });
if (user.verified) return res.json({ success: true, message: 'Already verified' });
if (user.verificationCode !== code || user.verificationExpires < new Date()) {
return res.status(400).json({ error: 'Invalid or expired code' });
}
await db.collection('users').updateOne({ email }, { $set: { verified: true }, $unset: { verificationCode: '', verificationExpires: '' } });
res.json({ success: true });
} catch (err) {
console.error('Verify error:', err);
res.status(500).json({ error: 'Verification failed' });
}
});
// Resend code
/**
* @swagger
* /auth/resend-code:
* post:
* tags: [auth]
* summary: Повторная отправка кода подтверждения
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* email:
* type: string
* responses:
* 200:
* description: OK
*/
router.post('/resend-code', async (req, res) => {
try {
const { email } = req.body;
const db = await getDb();
const user = await db.collection('users').findOne({ email });
if (!user) return res.status(400).json({ error: 'User not found' });
const code = generateCode();
const codeExpires = new Date(Date.now() + 10 * 60 * 1000);
await db.collection('users').updateOne({ email }, { $set: { verificationCode: code, verificationExpires: codeExpires } });
await sendVerificationEmail(email, code);
res.json({ success: true });
} catch (err) {
console.error('Resend code error:', err);
res.status(500).json({ error: 'Failed to resend code' });
}
});
// Login
/**
* @swagger
* /auth/login:
* post:
* tags: [auth]
* summary: Логин пользователя
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* email:
* type: string
* password:
* type: string
*
* responses:
* 200:
* description: JWT token
*/
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const db = await getDb();
const user = await db.collection('users').findOne({ email });
if (!user) return res.status(400).json({ error: 'User not found' });
if (!user.verified) {
return res.status(403).json({ error: 'Account not activated. Please verify your email.' });
}
const valid = await bcrypt.compare(password, user.password);
if (!valid) return res.status(400).json({ error: 'Invalid password' });
const payload = {
id: user._id.toString(),
email: user.email,
name: user.name || '',
verified: user.verified,
isAdmin: user.isAdmin,
adminVerified: user.adminVerified
};
const secret = process.env.JWT_SECRET || process.env.jwt_secret;
const token = jwt.sign(payload, secret, { expiresIn: '7d', jwtid: uuidv4() });
res.json({ token, user: { name: user.name || '', email: user.email } });
} catch (err) {
console.error('Login error:', err);
res.status(500).json({ error: 'Login failed' });
}
});
module.exports = router;