const express = require('express'); const { v4: uuidv4 } = require('uuid'); const nodemailer = require('nodemailer'); const config = require('../config'); const router = express.Router(); const createTransporter = () => { return nodemailer.createTransport({ host: config.email.host, port: config.email.port, auth: { user: config.email.user, pass: config.email.pass } }); }; // Mock email transporter for development const transporter = createTransporter(); module.exports = (pool, query) => { // Register new user router.post('/register', async (req, res, next) => { const { email, firstName, lastName } = req.body; try { // Check if user already exists const userCheck = await query( 'SELECT * FROM users WHERE email = $1', [email] ); if (userCheck.rows.length > 0) { return res.status(400).json({ error: true, message: 'User with this email already exists' }); } // Create new user const result = await query( 'INSERT INTO users (email, first_name, last_name) VALUES ($1, $2, $3) RETURNING id, email', [email, firstName, lastName] ); res.status(201).json({ message: 'User registered successfully', user: result.rows[0] }); } catch (error) { next(error); } }); // Request login code router.post('/login-request', async (req, res, next) => { const { email } = req.body; console.log('/login-request') console.log(JSON.stringify(config, null, 4)) try { // Check if user exists const userResult = await query( 'SELECT * FROM users WHERE email = $1', [email] ); if (userResult.rows.length === 0) { return res.status(404).json({ error: true, message: 'User not found' }); } const user = userResult.rows[0]; // Generate a random 6-digit code // Set expiration time (15 minutes from now) const authCode = Math.floor(100000 + Math.random() * 900000).toString(); const expiresAt = new Date(); expiresAt.setMinutes(expiresAt.getMinutes() + 15); // Create auth record const authResult = await query( 'INSERT INTO authentications (code, expires_at) VALUES ($1, $2) RETURNING id', [authCode, expiresAt] ); const authId = authResult.rows[0].id; // Update user with auth record await query( 'UPDATE users SET current_auth_id = $1 WHERE id = $2', [authId, user.id] ); // Send email with code (mock in development) const loginLink = `${config.site.protocol}://${config.site.domain}/verify?code=${authCode}&email=${encodeURIComponent(email)}`; await transporter.sendMail({ from: 'noreply@2many.ca', to: email, subject: 'Your Login Code', html: `
This code will expire in 15 minutes.
Or click here to log in directly.
` }); let retObj = { message: 'Login code sent to address: ' + email } if(process.env.ENVIRONMENT === "beta"){ retObj.code = authCode; } res.json(retObj); } catch (error) { next(error); } }); // Verify login code router.post('/verify', async (req, res, next) => { const { email, code } = req.body; try { // Get user and auth record const userResult = await query( `SELECT u.*, a.code, a.expires_at, a.is_used FROM users u JOIN authentications a ON u.current_auth_id = a.id WHERE u.email = $1`, [email] ); if (userResult.rows.length === 0) { return res.status(400).json({ error: true, message: 'Invalid request or no login code requested' }); } const { code: storedCode, expires_at, is_used, id: userId, is_disabled } = userResult.rows[0]; // Check if account is disabled if (is_disabled) { return res.status(403).json({ error: true, message: 'Your account has been disabled. Please contact support for assistance.' }); } // Check code if (storedCode !== code) { return res.status(400).json({ error: true, message: 'Invalid code' }); } // Check if expired if (new Date() > new Date(expires_at)) { return res.status(400).json({ error: true, message: 'Code has expired, Please restart login' }); } // Check if already used if (is_used) { return res.status(400).json({ error: true, message: 'Code has already been used' , data: is_used }); } // Mark auth as used await query( 'UPDATE authentications SET is_used = true WHERE code = $1', [code] ); // Generate API key const apiKey = uuidv4(); // Save API key to user await query( 'UPDATE users SET api_key = $1, last_login = NOW() WHERE id = $2', [apiKey, userId] ); // Get user information including admin status const userInfo = await query( 'SELECT id, email, first_name, last_name, is_admin FROM users WHERE id = $1', [userId] ); res.json({ message: 'Login successful', userId: userId, isAdmin: userInfo.rows[0].is_admin, firstName: userInfo.rows[0].first_name, lastName: userInfo.rows[0].last_name, email: userInfo.rows[0].email, apiKey: apiKey }); } catch (error) { next(error); } }); //apikey check router.post('/verify-key', async (req, res, next) => { const { apiKey, email } = req.body; try { // Verify the API key against email const result = await query( 'SELECT id, email, first_name, last_name, is_admin, is_disabled FROM users WHERE api_key = $1 AND email = $2', [apiKey, email] ); if (result.rows.length === 0) { return res.status(401).json({ error: true, message: 'Invalid API key' }); } if (result.rows[0].is_disabled) { return res.status(403).json({ error: true, message: 'Your account has been disabled. Please contact support for assistance.' }); } res.json({ valid: true, user: result.rows[0] }); } catch (error) { next(error); } }); // Logout router.post('/logout', async (req, res, next) => { const { userId } = req.body; try { // Get current auth ID const userResult = await query( 'SELECT current_auth_id FROM users WHERE id = $1', [userId] ); if (userResult.rows.length === 0) { return res.status(404).json({ error: true, message: 'User not found' }); } const authId = userResult.rows[0].current_auth_id; // Clear auth ID and API key await query( 'UPDATE users SET current_auth_id = NULL, api_key = NULL WHERE id = $1', [userId] ); // Delete auth record if (authId) { await query( 'DELETE FROM authentications WHERE id = $1', [authId] ); } res.json({ message: 'Logged out successfully' }); } catch (error) { next(error); } }); return router; };