CentralBackend/services/email/EmailVerificationService.js
Seazn c2bbb1df15 feat: implement auto-renewal cron service for subscriptions
- Added RenewalCronService to handle automatic subscription renewals and reactivations.
- Introduced listPausedAutoRenew method in AbonemmentRepository to fetch paused subscriptions eligible for reactivation.
- Created test script for renewal cron job to simulate subscription renewal scenarios.
- Updated MailService to send renewal confirmation and payment reminder emails.
- Enhanced EmailVerificationService to auto-grant 'can_subscribe' permission upon email verification.
- Modified createAdminUser script to allow different admin email configurations.
- Added node-cron dependency for scheduling tasks.
2026-03-15 14:16:46 +01:00

114 lines
4.7 KiB
JavaScript

const EmailVerificationRepository = require('../../repositories/email/EmailVerificationRepository');
const MailService = require('./MailService');
const { logger } = require('../../middleware/logger');
class EmailVerificationService {
static async sendVerificationEmail(user, unitOfWork) {
logger.info('EmailVerificationService.sendVerificationEmail:start', { userId: user.id, email: user.email });
const emailVerificationRepo = new EmailVerificationRepository(unitOfWork);
try {
// Check if already verified
const status = await unitOfWork.connection.query(
'SELECT email_verified FROM user_status WHERE user_id = ?', [user.id]
);
if (status[0][0] && status[0][0].email_verified) {
logger.warn('EmailVerificationService.sendVerificationEmail:already_verified', { userId: user.id });
throw new Error('Email already verified');
}
// Generate 6-digit code and expiry
const code = Math.floor(100000 + Math.random() * 900000).toString();
const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
// Upsert code in DB
await emailVerificationRepo.upsertCode(user.id, code, expiresAt);
logger.info('EmailVerificationService.sendVerificationEmail:code_upserted', { userId: user.id });
// Send email
await MailService.sendVerificationCodeEmail({
email: user.email,
code,
expiresAt
});
logger.info('EmailVerificationService.sendVerificationEmail:email_sent', { userId: user.id, email: user.email });
logger.info('EmailVerificationService.sendVerificationEmail:success', { userId: user.id });
return true;
} catch (error) {
logger.error('EmailVerificationService.sendVerificationEmail:error', { userId: user.id, error: error.message });
throw error;
}
}
static async verifyCode(userId, code, unitOfWork) {
logger.info('EmailVerificationService.verifyCode:start', { userId, code });
const emailVerificationRepo = new EmailVerificationRepository(unitOfWork);
try {
// Get latest, unexpired, unverified code
const record = await emailVerificationRepo.getByUserId(userId);
if (
!record ||
record.verified_at ||
new Date(record.expires_at) < new Date() ||
record.verification_code !== code
) {
logger.warn('EmailVerificationService.verifyCode:invalid_or_expired', { userId, code });
// Optionally increment attempts
if (record) {
await emailVerificationRepo.incrementAttempts(record.id);
}
return { success: false, error: 'Invalid or expired code' };
}
// Mark as verified
await emailVerificationRepo.setVerified(record.id);
logger.info('EmailVerificationService.verifyCode:code_verified', { userId });
// Update user_status
await unitOfWork.connection.query(
`UPDATE user_status SET email_verified = 1, email_verified_at = NOW() WHERE user_id = ?`,
[userId]
);
logger.info('EmailVerificationService.verifyCode:user_status_updated', { userId });
// Check if all steps are complete and set status to 'pending' if so
const UserStatusService = require('../status/UserStatusService');
await UserStatusService.checkAndSetPendingIfComplete(userId, unitOfWork);
logger.info('EmailVerificationService.verifyCode:pending_check_complete', { userId });
// Auto-grant can_subscribe permission if user doesn't already have it
try {
const [permRows] = await unitOfWork.connection.query(
`SELECT id FROM permissions WHERE name = 'can_subscribe' AND is_active = TRUE LIMIT 1`
);
if (permRows.length > 0) {
const permId = permRows[0].id;
const [existing] = await unitOfWork.connection.query(
`SELECT 1 FROM user_permissions WHERE user_id = ? AND permission_id = ? LIMIT 1`,
[userId, permId]
);
if (existing.length === 0) {
await unitOfWork.connection.query(
`INSERT INTO user_permissions (user_id, permission_id) VALUES (?, ?)`,
[userId, permId]
);
logger.info('EmailVerificationService.verifyCode:can_subscribe_granted', { userId });
}
}
} catch (permError) {
logger.error('EmailVerificationService.verifyCode:permission_grant_error', { userId, error: permError.message });
}
logger.info('EmailVerificationService.verifyCode:success', { userId });
return { success: true };
} catch (error) {
logger.error('EmailVerificationService.verifyCode:error', { userId, error: error.message });
throw error;
}
}
}
module.exports = EmailVerificationService;