287 lines
No EOL
7.6 KiB
JavaScript
287 lines
No EOL
7.6 KiB
JavaScript
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: `
|
|
<h1>Your login code is: ${authCode}</h1>
|
|
<p>This code will expire in 15 minutes.</p>
|
|
<p>Or click <a href="${loginLink}">here</a> to log in directly.</p>
|
|
`
|
|
});
|
|
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;
|
|
}; |