Send activation link immediately for walk-in and manual registration accounts
Accounts created by staff on someone's behalf now get their activation link (email or WhatsApp) sent right away, instead of only on a first failed login attempt, matching what the Terms of Use already promised. This also fixed a real account with a real email being silently activated with a fixed, undisclosed password (Hope123). Also fixes the self-service kiosk's "Create an account" password field, which never actually took effect server-side, and removes the "Guest (no account)" checkboxes that no longer had any backend effect once every walk-in account started behaving the same way. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
const prisma = require('../config/db');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const axios = require("axios");
|
||||
const { generateTicketsForRegistration } = require('../utils/ticketUtils');
|
||||
const { emailTickets } = require('./ticketController');
|
||||
const { hashPassword } = require('../config/auth');
|
||||
@@ -760,7 +759,7 @@ const getRegistrationsByEvent = async (req, res) => {
|
||||
const createManualRegistration = async (req, res) => {
|
||||
let userRecord;
|
||||
try {
|
||||
const { eventId, options, user, guestOnly, notificationPreference: prefFromBody } = req.body;
|
||||
const { eventId, options, user, notificationPreference: prefFromBody, skipActivationNotice } = req.body;
|
||||
|
||||
if (!eventId || !options || !user || !user.name || (!user.email && !user.phoneNumber)) {
|
||||
res.status(400);
|
||||
@@ -844,7 +843,7 @@ const createManualRegistration = async (req, res) => {
|
||||
? prefFromBody
|
||||
: (hasValidEmail && phone ? 'both' : phone ? 'whatsapp' : 'email');
|
||||
|
||||
// Always search by email AND/OR phone regardless of guestOnly.
|
||||
// Always search by email AND/OR phone.
|
||||
// Resolve each channel independently (rather than a single findFirst with an OR
|
||||
// across both) so that an email belonging to one account and a phone number
|
||||
// belonging to a *different* account can never be silently collapsed into
|
||||
@@ -907,42 +906,58 @@ const createManualRegistration = async (req, res) => {
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await prisma.user.update({ where: { id: userId }, data: updateData }).catch(() => {});
|
||||
}
|
||||
} else if (!guestOnly && hasValidEmail) {
|
||||
// Create a real active account (non-guest with email)
|
||||
try {
|
||||
const password = 'Hope123';
|
||||
const response = await axios.post(
|
||||
`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000'}/api/users`,
|
||||
{ name: user.name, email: user.email, password, phoneNumber: phone || null }
|
||||
);
|
||||
const createdUser = response.data.user || response.data;
|
||||
if (!createdUser?.id) { res.status(400); throw new Error('User creation failed: No user ID returned'); }
|
||||
userId = createdUser.id;
|
||||
// Set derived preference on the new account
|
||||
await prisma.user.update({ where: { id: userId }, data: { notificationPreference: derivedPref } }).catch(() => {});
|
||||
} catch (userErr) {
|
||||
res.status(400);
|
||||
throw new Error(`Failed to create user: ${userErr.response?.data?.message || userErr.message}`);
|
||||
}
|
||||
} else {
|
||||
// Guest path: phone-only, guestOnly=true, or no valid email
|
||||
const placeholderEmail = hasValidEmail
|
||||
? user.email
|
||||
: `guest+${uuidv4().slice(0, 8)}@guest.local`;
|
||||
const hashed = await hashPassword(uuidv4());
|
||||
const created = await prisma.user.create({
|
||||
data: {
|
||||
id: uuidv4(),
|
||||
name: user.name,
|
||||
email: placeholderEmail,
|
||||
password: hashed,
|
||||
phoneNumber: phone || null,
|
||||
isActive: false,
|
||||
notificationPreference: derivedPref,
|
||||
updatedAt: new Date(),
|
||||
const suppliedPassword = typeof user.password === 'string' && user.password.trim().length >= 6
|
||||
? user.password.trim()
|
||||
: null;
|
||||
|
||||
if (hasValidEmail && suppliedPassword) {
|
||||
// Caller supplied their own password (the self-service kiosk, where the
|
||||
// visitor sets it themselves on the spot) — activate immediately, since
|
||||
// there's nothing left for them to do via an activation link.
|
||||
const hashed = await hashPassword(suppliedPassword);
|
||||
const created = await prisma.user.create({
|
||||
data: {
|
||||
id: uuidv4(),
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
password: hashed,
|
||||
phoneNumber: phone || null,
|
||||
isActive: true,
|
||||
notificationPreference: derivedPref,
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
});
|
||||
userId = created.id;
|
||||
} else {
|
||||
// New account: uses the real email if a valid one was given, otherwise a
|
||||
// guest.local placeholder (phone-only registration). Always created inactive
|
||||
// with a random password — the visitor activates it themselves via the link
|
||||
// sent immediately below (email or WhatsApp), unless the caller explicitly
|
||||
// opted out of that nudge (e.g. a self-service visitor who declined to
|
||||
// create an account at all).
|
||||
const placeholderEmail = hasValidEmail
|
||||
? user.email
|
||||
: `guest+${uuidv4().slice(0, 8)}@guest.local`;
|
||||
const hashed = await hashPassword(uuidv4());
|
||||
const created = await prisma.user.create({
|
||||
data: {
|
||||
id: uuidv4(),
|
||||
name: user.name,
|
||||
email: placeholderEmail,
|
||||
password: hashed,
|
||||
phoneNumber: phone || null,
|
||||
isActive: false,
|
||||
notificationPreference: derivedPref,
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
});
|
||||
userId = created.id;
|
||||
if (!skipActivationNotice) {
|
||||
const { sendActivationLink } = require('./userController');
|
||||
sendActivationLink(created);
|
||||
}
|
||||
});
|
||||
userId = created.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge into existing non-cancelled registration if one exists, otherwise create new
|
||||
|
||||
Reference in New Issue
Block a user