diff --git a/controllers/auth.controller.js b/controllers/auth.controller.js index 7cbdc60..e71738b 100644 --- a/controllers/auth.controller.js +++ b/controllers/auth.controller.js @@ -120,12 +120,56 @@ exports.register = async (req, res) => { const transaction = await sequelize.transaction(); try { - const { email, password, personal_info, group_code } = req.body; - + const { email, password, personal_info, group_code, confirm_resume } = req.body; + // ── Duplicate check ─────────────────────────────────────────────────────── + // A verified account owns the email outright — hard block. An unverified + // one is just an abandoned attempt (e.g. dropped connection before the OTP + // step completed, or the client retried after a failed send) — the same + // person retrying should be able to resume it rather than dead-end here. + // The client must explicitly confirm_resume (after the user accepts a + // confirmation dialog) before we overwrite that abandoned attempt's data. const existing = await mdl_Users.findOne({ where: { email } }); - if (existing) return R.error(res, 'Email is already registered.', 409); - + if (existing) { + if (existing.is_verified) { + await transaction.rollback(); + return R.error(res, 'Email is already registered.', 409); + } + if (existing.reg_type !== 'system') { + await transaction.rollback(); + return R.error(res, 'This email is linked to a Google account. Please sign in with Google instead.', 409, { google: true }); + } + + if (!confirm_resume) { + await transaction.rollback(); + return R.error( + res, + 'An account with this email already has a pending verification. Resend the code and continue?', + 409, + { pendingVerification: true }, + ); + } + + const hashed = await bcrypt.hash(password, 12); + const otp = generateOTP(); + + await existing.update({ + password: hashed, + personal_info: personal_info ?? existing.personal_info, + otp_code: otp, + otp_expires_at: getOTPExpiry(), + }, { transaction }); + + await transaction.commit(); + + sendEmail({ to: email, type: 'OTP', data: { otp } }) + .catch(err => console.error('[AUTH] Failed to send OTP email:', err)); + + return R.success(res, 'Registration successful. Please check your email for the OTP.', { + email: existing.email, + }, 201); + } + // ── Validate group_code if provided ─────────────────────────────────────── let group = null; if (group_code) {