const nocodbService = require('../services/nocodb'); const { sendLoginDetails } = require('../services/email'); const { sanitizeUser, extractId } = require('../utils/helpers'); class UsersController { async getAll(req, res) { try { console.log('UsersController.getAll called'); console.log('Users table ID:', nocodbService.tableIds.users); if (!nocodbService.tableIds.users) { console.error('Users table not configured in environment'); return res.status(500).json({ success: false, error: 'Users table not configured. Please set NOCODB_TABLE_USERS in your environment variables.' }); } console.log('Fetching users from NocoDB...'); const response = await nocodbService.getAll(nocodbService.tableIds.users, { limit: 100 }); const users = response.list || []; console.log(`Retrieved ${users.length} users from database`); // Remove password field from response for security const safeUsers = users.map(sanitizeUser); res.json({ success: true, users: safeUsers }); } catch (error) { console.error('Error fetching users:', error); res.status(500).json({ success: false, error: 'Failed to fetch users: ' + error.message }); } } async create(req, res) { try { const { email, password, name, phone, isAdmin, userType, expireDays } = req.body; if (!email || !password) { return res.status(400).json({ success: false, error: 'Email and password are required' }); } if (!nocodbService.tableIds.users) { return res.status(500).json({ success: false, error: 'Users table not configured' }); } // Check if user already exists console.log(`Checking if user exists with email: ${email}`); let existingUser = null; try { existingUser = await nocodbService.getUserByEmail(email); console.log('Existing user check result:', existingUser ? 'User exists' : 'User does not exist'); } catch (error) { console.error('Error checking for existing user:', error.message); // Continue with creation if check fails - NocoDB will handle the unique constraint } if (existingUser) { console.log('Existing user found:', { id: existingUser.ID || existingUser.Id || existingUser.id, email: existingUser.Email || existingUser.email }); return res.status(400).json({ success: false, error: 'User with this email already exists' }); } // Calculate expiration date for temp users let expiresAt = null; if (userType === 'temp' && expireDays) { const expirationDate = new Date(); expirationDate.setDate(expirationDate.getDate() + expireDays); expiresAt = expirationDate.toISOString(); } // Create new user - use the exact column titles from NocoDB schema const userData = { Email: email, Name: name || '', Password: password, Phone: phone || '', Admin: isAdmin === true, 'User Type': userType || 'user', ExpiresAt: expiresAt, ExpireDays: userType === 'temp' ? expireDays : null, 'Last Login': null }; const response = await nocodbService.create( nocodbService.tableIds.users, userData ); res.status(201).json({ success: true, message: 'User created successfully', user: { id: extractId(response), email: email, name: name, phone: phone, admin: isAdmin, userType: userType, expiresAt: expiresAt } }); } catch (error) { console.error('Error creating user:', error); // Check if it's a unique constraint violation (email already exists) if (error.response?.data?.code === '23505' || error.response?.data?.message?.includes('already exists') || error.message?.includes('already exists')) { return res.status(400).json({ success: false, error: 'A user with this email address already exists' }); } res.status(500).json({ success: false, error: 'Failed to create user: ' + (error.message || 'Unknown error') }); } } async delete(req, res) { try { const userId = req.params.id; if (!nocodbService.tableIds.users) { return res.status(500).json({ success: false, error: 'Users table not configured' }); } // Don't allow admins to delete themselves if (userId === req.session.userId) { return res.status(400).json({ success: false, error: 'Cannot delete your own account' }); } await nocodbService.deleteUser(userId); res.json({ success: true, message: 'User deleted successfully' }); } catch (error) { console.error('Error deleting user:', error); res.status(500).json({ success: false, error: 'Failed to delete user' }); } } async sendLoginDetails(req, res) { try { const userId = req.params.id; if (!nocodbService.tableIds.users) { return res.status(500).json({ success: false, error: 'Users table not configured' }); } // Get user data from database const user = await nocodbService.getById( nocodbService.tableIds.users, userId ); if (!user) { return res.status(404).json({ success: false, error: 'User not found' }); } // Send login details email await sendLoginDetails(user); res.json({ success: true, message: 'Login details sent successfully' }); } catch (error) { console.error('Error sending login details:', error); res.status(500).json({ success: false, error: 'Failed to send login details' }); } } async emailAllUsers(req, res) { try { const { subject, content } = req.body; if (!subject || !content) { return res.status(400).json({ success: false, error: 'Subject and content are required' }); } if (!nocodbService.tableIds.users) { return res.status(500).json({ success: false, error: 'Users table not configured' }); } // Get all users const response = await nocodbService.getAll(nocodbService.tableIds.users, { limit: 1000 }); const users = response.list || []; if (users.length === 0) { return res.status(400).json({ success: false, error: 'No users found to email' }); } // Import email service const { sendEmail } = require('../services/email'); const emailTemplates = require('../services/emailTemplates'); // Convert rich text content to plain text for the text version const stripHtmlTags = (html) => { return html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').trim(); }; // Prepare base template variables const baseTemplateVariables = { APP_NAME: 'BNKops Influence - User Broadcast', EMAIL_SUBJECT: subject, EMAIL_CONTENT: content, EMAIL_CONTENT_TEXT: stripHtmlTags(content), SENDER_NAME: req.session.userName || req.session.userEmail || 'Administrator', TIMESTAMP: new Date().toLocaleString() }; // Send emails to all users const emailResults = []; const failedEmails = []; for (const user of users) { try { const userVariables = { ...baseTemplateVariables, USER_NAME: user.Name || user.name || user.Email || user.email || 'User', USER_EMAIL: user.Email || user.email }; const emailContent = await emailTemplates.render('user-broadcast', userVariables); await sendEmail({ to: user.Email || user.email, subject: subject, text: emailContent.text, html: emailContent.html }); emailResults.push({ email: user.Email || user.email, name: user.Name || user.name || user.Email || user.email, success: true }); console.log(`Sent broadcast email to: ${user.Email || user.email}`); } catch (emailError) { console.error(`Failed to send broadcast email to ${user.Email || user.email}:`, emailError); failedEmails.push({ email: user.Email || user.email, name: user.Name || user.name || user.Email || user.email, error: emailError.message }); } } const successCount = emailResults.length; const failCount = failedEmails.length; if (successCount === 0) { return res.status(500).json({ success: false, error: 'Failed to send any emails', details: failedEmails }); } res.json({ success: true, message: `Sent email to ${successCount} user${successCount !== 1 ? 's' : ''}${failCount > 0 ? `, ${failCount} failed` : ''}`, results: { successful: emailResults, failed: failedEmails, total: users.length, subject: subject } }); } catch (error) { console.error('Error sending broadcast email:', error); res.status(500).json({ success: false, error: 'Failed to send broadcast email' }); } } } module.exports = new UsersController();