Initial commit: Club DynaSQY
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
const pool = require('../config/db');
|
||||
const crypto = require('crypto');
|
||||
const { sendInvitationEmail } = require('../config/email');
|
||||
const { logActivity } = require('../services/activityLog');
|
||||
|
||||
exports.list = async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT i.*, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom
|
||||
FROM invites i
|
||||
LEFT JOIN users u ON u.id = i.invite_par_id
|
||||
WHERE i.statut_candidature IS NULL OR i.statut_candidature != 'accepte'
|
||||
ORDER BY i.date_club DESC, i.created_at DESC
|
||||
`);
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getById = async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.query('SELECT * FROM invites WHERE id = ?', [req.params.id]);
|
||||
if (rows.length === 0) return res.status(404).json({ error: 'Not found' });
|
||||
res.json(rows[0]);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.search = async (req, res) => {
|
||||
try {
|
||||
const { q } = req.query;
|
||||
if (!q || q.length < 2) return res.json([]);
|
||||
const [rows] = await pool.query(`
|
||||
SELECT prenom, nom, societe, activite, telephone, email, site_web,
|
||||
MAX(visit_number) AS visit_number, COUNT(*) AS total_visits
|
||||
FROM invites
|
||||
WHERE CONCAT(prenom, ' ', nom) LIKE ?
|
||||
GROUP BY prenom, nom, societe, activite, telephone, email, site_web
|
||||
ORDER BY total_visits DESC LIMIT 20
|
||||
`, [`%${q}%`]);
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
const BLOCKED_DATES = ['2026-10-20', '2026-12-15'];
|
||||
|
||||
exports.create = async (req, res) => {
|
||||
try {
|
||||
const { prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club } = req.body;
|
||||
if (!prenom || !nom || !telephone || !email || !date_club) {
|
||||
return res.status(400).json({ error: 'Tous les champs sauf commentaires sont requis' });
|
||||
}
|
||||
if (BLOCKED_DATES.includes(date_club)) {
|
||||
return res.status(403).json({ error: "Cette date n'accepte pas de nouveaux invités" });
|
||||
}
|
||||
const invite_par_id = req.user.id;
|
||||
|
||||
// Determine visit_number
|
||||
const [existing] = await pool.query(
|
||||
'SELECT MAX(visit_number) AS max_vn FROM invites WHERE prenom = ? AND nom = ?',
|
||||
[prenom, nom]
|
||||
);
|
||||
const visit_number = (existing[0].max_vn || 0) + 1;
|
||||
|
||||
// Only admin can add 3rd+ visit
|
||||
if (visit_number >= 3 && req.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'Seul un administrateur peut ajouter une 3e visite ou plus' });
|
||||
}
|
||||
|
||||
const [result] = await pool.query(
|
||||
`INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[prenom, nom, societe || null, activite || null, telephone, email, site_web || null, commentaires || null, date_club, invite_par_id, visit_number]
|
||||
);
|
||||
logActivity(req, 'invite_ajout', { prenom, nom });
|
||||
res.status(201).json({ id: result.insertId, message: 'Guest added', visit_number });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.update = async (req, res) => {
|
||||
try {
|
||||
const guestId = req.params.id;
|
||||
const [existing] = await pool.query('SELECT invite_par_id FROM invites WHERE id = ?', [guestId]);
|
||||
if (existing.length === 0) return res.status(404).json({ error: 'Not found' });
|
||||
if (existing[0].invite_par_id !== req.user.id && req.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'You can only edit your own guests' });
|
||||
}
|
||||
const { prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club } = req.body;
|
||||
if (BLOCKED_DATES.includes(date_club)) {
|
||||
return res.status(403).json({ error: "Cette date n'accepte pas de nouveaux invités" });
|
||||
}
|
||||
await pool.query(
|
||||
`UPDATE invites SET prenom=?, nom=?, societe=?, activite=?, telephone=?, email=?, site_web=?, commentaires=?, date_club=? WHERE id=?`,
|
||||
[prenom, nom, societe || null, activite || null, telephone, email, site_web || null, commentaires || null, date_club, guestId]
|
||||
);
|
||||
logActivity(req, 'invite_modification', { guestId, prenom, nom });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.remove = async (req, res) => {
|
||||
try {
|
||||
const guestId = req.params.id;
|
||||
const [existing] = await pool.query('SELECT invite_par_id FROM invites WHERE id = ?', [guestId]);
|
||||
if (existing.length === 0) return res.status(404).json({ error: 'Not found' });
|
||||
if (existing[0].invite_par_id !== req.user.id && req.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'Vous ne pouvez supprimer que vos propres invités' });
|
||||
}
|
||||
await pool.query('DELETE FROM invites WHERE id = ?', [guestId]);
|
||||
logActivity(req, 'invite_suppression', { guestId });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.sendInvitation = async (req, res) => {
|
||||
try {
|
||||
const guestId = req.params.id;
|
||||
const [guests] = await pool.query(
|
||||
`SELECT i.*, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom
|
||||
FROM invites i
|
||||
LEFT JOIN users u ON u.id = i.invite_par_id
|
||||
WHERE i.id = ?`,
|
||||
[guestId]
|
||||
);
|
||||
if (guests.length === 0) return res.status(404).json({ error: 'Invité non trouvé' });
|
||||
const guest = guests[0];
|
||||
if (guest.invite_par_id !== req.user.id && req.user.role !== 'admin') {
|
||||
return res.status(403).json({ error: 'Vous ne pouvez envoyer que vos propres invitations' });
|
||||
}
|
||||
if (!guest.email) {
|
||||
return res.status(400).json({ error: "Cet invité n'a pas d'adresse email" });
|
||||
}
|
||||
const token = crypto.randomBytes(32).toString('hex');
|
||||
const baseUrl = process.env.PUBLIC_URL || 'https://dyna.secourit.fr';
|
||||
const [inviter] = await pool.query('SELECT prenom, nom FROM users WHERE id = ?', [guest.invite_par_id]);
|
||||
await sendInvitationEmail(guest, inviter[0], token, baseUrl);
|
||||
await pool.query(
|
||||
'UPDATE invites SET token=?, mail_envoye=1, invitation_sent_at=NOW(), statut="en_attente" WHERE id=?',
|
||||
[token, guestId]
|
||||
);
|
||||
logActivity(req, 'invite_email_envoye', { guestId, email: guest.email });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Erreur lors de l'envoi de l'email" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.candidature = async (req, res) => {
|
||||
try {
|
||||
if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' });
|
||||
const guestId = req.params.id;
|
||||
const { action } = req.body;
|
||||
if (!['start', 'accept', 'reject'].includes(action)) {
|
||||
return res.status(400).json({ error: 'Action invalide (start/accept/reject)' });
|
||||
}
|
||||
const [guests] = await pool.query('SELECT * FROM invites WHERE id = ?', [guestId]);
|
||||
if (guests.length === 0) return res.status(404).json({ error: 'Invité non trouvé' });
|
||||
const guest = guests[0];
|
||||
|
||||
if (action === 'start') {
|
||||
await pool.query(
|
||||
'UPDATE invites SET statut_candidature = ?, candidature_date = NOW() WHERE id = ?',
|
||||
['candidature', guestId]
|
||||
);
|
||||
logActivity(req, 'candidature_demarrage', { guestId, prenom: guest.prenom, nom: guest.nom });
|
||||
return res.json({ message: 'Candidature en cours', statut_candidature: 'candidature' });
|
||||
}
|
||||
|
||||
if (action === 'reject') {
|
||||
await pool.query(
|
||||
'UPDATE invites SET statut_candidature = ? WHERE id = ?',
|
||||
['refuse', guestId]
|
||||
);
|
||||
logActivity(req, 'candidature_refus', { guestId, prenom: guest.prenom, nom: guest.nom });
|
||||
return res.json({ message: 'Candidature refusée', statut_candidature: 'refuse' });
|
||||
}
|
||||
|
||||
if (action === 'accept') {
|
||||
const bcrypt = require('bcrypt');
|
||||
const hash = await bcrypt.hash('Bienvenue2026', 10);
|
||||
const [result] = await pool.query(
|
||||
`INSERT INTO users (prenom, nom, societe, activite, telephone, email, site_web, role, password_hash, date_adhesion, invite_par_id, actif)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 'member', ?, CURDATE(), ?, 1)`,
|
||||
[guest.prenom, guest.nom, guest.societe || null, guest.activite || null,
|
||||
guest.telephone || null, guest.email || null, guest.site_web || null,
|
||||
hash, guest.invite_par_id]
|
||||
);
|
||||
await pool.query('DELETE FROM invites WHERE id = ?', [guestId]);
|
||||
logActivity(req, 'candidature_acceptee', { guestId, prenom: guest.prenom, nom: guest.nom });
|
||||
return res.json({ message: 'Membre créé avec succès', user_id: result.insertId, defaultPassword: 'Bienvenue2026' });
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code === 'ER_DUP_ENTRY') {
|
||||
return res.status(400).json({ error: 'Cet email est déjà utilisé par un membre existant' });
|
||||
}
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Erreur serveur' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.nextEventGuestCount = async (req, res) => {
|
||||
try {
|
||||
const [events] = await pool.query(
|
||||
'SELECT date FROM evenements WHERE date >= CURDATE() ORDER BY date ASC LIMIT 1'
|
||||
);
|
||||
if (events.length === 0) return res.json({ count: 0, next_event_date: null });
|
||||
const nextDate = events[0].date;
|
||||
const [rows] = await pool.query(
|
||||
'SELECT COUNT(*) AS count FROM invites WHERE date_club = ?',
|
||||
[nextDate]
|
||||
);
|
||||
res.json({ count: rows[0].count, next_event_date: nextDate });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user