the-tip-top-backend/src/controllers/newsletter.controller.js
soufiane 4759ce99e7 feat: add newsletter subscription feature
- Add newsletter database table migration
- Create newsletter controller with subscribe/unsubscribe endpoints
- Add newsletter routes and validation
- Implement newsletter service with email validation
- Add setup documentation and migration scripts
- Include test page for newsletter functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 00:07:44 +01:00

155 lines
3.8 KiB
JavaScript

/**
* Controller Newsletter
*/
import { pool } from '../../db.js';
import { AppError, asyncHandler } from '../middleware/errorHandler.js';
import { sendNewsletterConfirmationEmail } from '../services/newsletter.service.js';
/**
* S'abonner à la newsletter
* POST /api/newsletter/subscribe
*/
export const subscribe = asyncHandler(async (req, res, next) => {
const { email } = req.body;
if (!email) {
return next(new AppError('Email requis', 400));
}
// Vérifier si l'email existe déjà
const existingSubscription = await pool.query(
'SELECT * FROM newsletters WHERE email = $1',
[email]
);
if (existingSubscription.rows.length > 0) {
const subscription = existingSubscription.rows[0];
// Si déjà abonné et actif
if (subscription.is_active) {
return res.json({
success: true,
message: 'Vous êtes déjà abonné à notre newsletter',
});
}
// Si désabonné, réactiver l'abonnement
await pool.query(
'UPDATE newsletters SET is_active = TRUE, unsubscribed_at = NULL, updated_at = NOW() WHERE email = $1',
[email]
);
// Envoyer l'email de confirmation
try {
await sendNewsletterConfirmationEmail(email);
} catch (error) {
console.error('Erreur envoi email confirmation:', error);
// On continue même si l'email échoue
}
return res.json({
success: true,
message: 'Votre abonnement à la newsletter a été réactivé avec succès',
});
}
// Créer un nouvel abonnement
await pool.query(
'INSERT INTO newsletters (email, is_active) VALUES ($1, TRUE)',
[email]
);
// Envoyer l'email de confirmation
try {
await sendNewsletterConfirmationEmail(email);
} catch (error) {
console.error('Erreur envoi email confirmation:', error);
// On continue même si l'email échoue
}
res.status(201).json({
success: true,
message: 'Merci ! Vous êtes maintenant abonné à notre newsletter',
});
});
/**
* Se désabonner de la newsletter
* POST /api/newsletter/unsubscribe
*/
export const unsubscribe = asyncHandler(async (req, res, next) => {
const { email } = req.body;
if (!email) {
return next(new AppError('Email requis', 400));
}
// Vérifier si l'abonnement existe
const subscription = await pool.query(
'SELECT * FROM newsletters WHERE email = $1',
[email]
);
if (subscription.rows.length === 0) {
return next(new AppError('Aucun abonnement trouvé pour cet email', 404));
}
// Désactiver l'abonnement
await pool.query(
'UPDATE newsletters SET is_active = FALSE, unsubscribed_at = NOW(), updated_at = NOW() WHERE email = $1',
[email]
);
res.json({
success: true,
message: 'Vous avez été désabonné de notre newsletter',
});
});
/**
* Obtenir tous les abonnés (Admin seulement)
* GET /api/newsletter/subscribers
*/
export const getSubscribers = asyncHandler(async (req, res) => {
const result = await pool.query(
`SELECT id, email, is_active, subscribed_at, unsubscribed_at, created_at
FROM newsletters
ORDER BY created_at DESC`
);
res.json({
success: true,
count: result.rows.length,
data: result.rows.map(row => ({
id: row.id,
email: row.email,
isActive: row.is_active,
subscribedAt: row.subscribed_at,
unsubscribedAt: row.unsubscribed_at,
createdAt: row.created_at,
})),
});
});
/**
* Obtenir le nombre d'abonnés actifs (Admin seulement)
* GET /api/newsletter/count
*/
export const getActiveCount = asyncHandler(async (req, res) => {
const result = await pool.query(
'SELECT COUNT(*) as count FROM newsletters WHERE is_active = TRUE'
);
res.json({
success: true,
count: parseInt(result.rows[0].count),
});
});
export default {
subscribe,
unsubscribe,
getSubscribers,
getActiveCount,
};