- aggiornata la grafica della Home di RISO

- Profilo Completition
- Email Verificata
- Invita un Amico (invio di email)
This commit is contained in:
Surya Paolo
2025-11-15 19:38:55 +01:00
parent 26a42b1f30
commit adf1aac10f
312 changed files with 12061 additions and 81773 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
// @ts-check
const EventModel = require('../models/Event');
const { pick } = require('../utils/pick');
async function listEvents(req, res) {
const { limit = '6', page = '1', from, to, sort = 'start' } = /** @type {any} */ (req.query);
const q = {};
if (from) q.start = { ...(q.start || {}), $gte: new Date(from) };
if (to) q.start = { ...(q.start || {}), $lte: new Date(to) };
const lim = Math.min(Number(limit) || 6, 50);
const skip = (Math.max(Number(page) || 1, 1) - 1) * lim;
const [items, total] = await Promise.all([
EventModel.find(q)
.sort({ [sort]: 1 })
.skip(skip)
.limit(lim)
.lean(),
EventModel.countDocuments(q),
]);
res.set('Cache-Control', 'public, max-age=30, stale-while-revalidate=120');
return res.json({ items, total, page: Number(page), limit: lim });
}
async function createEvent(req, res) {
const data = pick(req.body, ['title', 'start', 'end', 'place', 'teaser', 'cover', 'to']);
const doc = await EventModel.create(data);
return res.status(201).json(doc);
}
async function updateEvent(req, res) {
const data = pick(req.body, ['title', 'start', 'end', 'place', 'teaser', 'cover', 'to']);
const doc = await EventModel.findByIdAndUpdate(req.params.id, data, { new: true });
if (!doc) return res.status(404).json({ message: 'Evento non trovato' });
return res.json(doc);
}
async function deleteEvent(req, res) {
const r = await EventModel.findByIdAndDelete(req.params.id);
if (!r) return res.status(404).json({ message: 'Evento non trovato' });
return res.status(204).send();
}
module.exports = { listEvents, createEvent, updateEvent, deleteEvent };

View File

@@ -0,0 +1,18 @@
// @ts-check
const { HomeModel } = require('../models/Home');
async function getHome(req, res) {
const doc = await HomeModel.findOne({});
if (!doc) return res.status(404).json({ message: 'Home non configurata' });
res.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=300');
res.set('ETag', `"${doc.updatedAt?.getTime?.() || Date.now()}"`);
return res.json(doc);
}
async function upsertHome(req, res) {
const payload = req.body || {};
const doc = await HomeModel.findOneAndUpdate({}, payload, { upsert: true, new: true, setDefaultsOnInsert: true });
return res.json(doc);
}
module.exports = { getHome, upsertHome };

View File

@@ -0,0 +1,20 @@
// @ts-check
async function subscribe(req, res) {
const { email } = req.body || {};
if (!email || !/.+@.+\..+/.test(email)) {
return res.status(400).json({ message: 'Email non valida' });
}
// Integra qui Mailchimp/Sendinblue se necessario
return res.status(200).json({ ok: true });
}
async function unsubscribe(req, res) {
const { email } = req.body || {};
if (!email || !/.+@.+\..+/.test(email)) {
return res.status(400).json({ message: 'Email non valida' });
}
// Integra qui Mailchimp/Sendinblue se necessario
return res.status(200).json({ ok: true });
}
module.exports = { subscribe, unsubscribe };

View File

@@ -0,0 +1,41 @@
// @ts-check
const { PostModel } = require('../models/Post');
const { pick } = require('../utils/pick');
async function listPosts(req, res) {
const { limit = '3', page = '1', sort = '-date', category } = /** @type {any} */ (req.query);
const q = {};
if (category) q.category = category;
const lim = Math.min(Number(limit) || 3, 50);
const skip = (Math.max(Number(page) || 1, 1) - 1) * lim;
const [items, total] = await Promise.all([
PostModel.find(q).sort(sort).skip(skip).limit(lim).lean(),
PostModel.countDocuments(q)
]);
res.set('Cache-Control', 'public, max-age=30, stale-while-revalidate=120');
return res.json({ items, total, page: Number(page), limit: lim });
}
async function createPost(req, res) {
const data = pick(req.body, ['title','date','category','teaser','cover','to','bodyMd']);
const doc = await PostModel.create(data);
return res.status(201).json(doc);
}
async function updatePost(req, res) {
const data = pick(req.body, ['title','date','category','teaser','cover','to','bodyMd']);
const doc = await PostModel.findByIdAndUpdate(req.params.id, data, { new: true });
if (!doc) return res.status(404).json({ message: 'Post non trovato' });
return res.json(doc);
}
async function deletePost(req, res) {
const r = await PostModel.findByIdAndDelete(req.params.id);
if (!r) return res.status(404).json({ message: 'Post non trovato' });
return res.status(204).send();
}
module.exports = { listPosts, createPost, updatePost, deletePost };

View File

@@ -0,0 +1,294 @@
// telegram.controller.js
const crypto = require('crypto');
const axios = require('axios');
const { User } = require('../models/user');
// Configurazione
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const TELEGRAM_API_URL = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}`;
/**
* Genera un token di verifica univoco
*/
exports.generateVerificationToken = async (req, res) => {
try {
const { username, idapp } = req.body;
if (!username) {
return res.status(400).json({ error: 'Username richiesto' });
}
// Verifica che l'utente esista
const user = await User.findOne({ idapp, username });
if (!user) {
return res.status(404).json({ error: 'Utente non trovato' });
}
// Verifica se già verificato
if (user.profile?.username_telegram && user.profile?.teleg_id) {
return res.status(400).json({ error: 'Account Telegram già collegato' });
}
// Genera token univoco
const token = crypto.randomBytes(32).toString('hex');
// Salva SOLO sul database (non serve la Map in-memory!)
await User.updateOne(
{ _id: user._id },
{
$set: {
'profile.telegram_verification_token': token,
'profile.telegram_verification_expires': new Date(Date.now() + 20 * 60 * 1000),
'profile.telegram_verified': false,
},
}
);
res.json({ token });
} catch (error) {
console.error('Errore generazione token:', error);
return res.status(500).json({ error: 'Errore del server' });
}
};
/**
* Controlla se la verifica è stata completata
*/
exports.checkVerification = async (req, res) => {
try {
const { token } = req.query;
if (!token) {
return res.status(400).json({ error: 'Token richiesto' });
}
// Cerca l'utente con questo token DIRETTAMENTE sul DB
const user = await User.findOne({
'profile.telegram_verification_token': token,
});
if (!user) {
return res.status(404).json({ error: 'Token non valido', verified: false });
}
// Verifica se è scaduto
const now = new Date();
if (now > user.profile.telegram_verification_expires) {
// Pulisci il token scaduto
await User.updateOne(
{ _id: user._id },
{
$unset: {
'profile.telegram_verification_token': '',
'profile.telegram_verification_expires': '',
},
}
);
return res.status(410).json({ error: 'Token scaduto', verified: false });
}
// Controlla se è stato verificato
const verified = !!(user.profile?.teleg_id && user.profile?.username_telegram);
res.json({
verified: verified,
username_telegram: user.profile?.username_telegram || null,
teleg_id: user.profile?.teleg_id || null,
});
} catch (error) {
console.error('Errore verifica token:', error);
return res.status(500).json({ error: 'Errore del server' });
}
};
/**
* Webhook del bot Telegram
* Gestisce i messaggi /start con il token
*/
exports.telegramWebhook = async (req, res) => {
try {
const update = req.body;
// Gestisci solo messaggi con /start
if (update.message && update.message.text && update.message.text.startsWith('/start')) {
const chatId = update.message.chat.id;
const userId = update.message.from.id;
const username = update.message.from.username;
const firstName = update.message.from.first_name;
// Estrai il token dal comando /start
const parts = update.message.text.split(' ');
const token = parts[1];
if (!token) {
await sendTelegramMessage(
chatId,
"⚠️ Link di verifica non valido. Richiedi un nuovo link dall'applicazione."
);
return res.sendStatus(200);
}
// Cerca l'utente con questo token
const user = await User.findOne({
'profile.telegram_verification_token': token,
});
if (!user) {
await sendTelegramMessage(
chatId,
"❌ Token non valido o scaduto. Richiedi un nuovo link dall'applicazione."
);
return res.sendStatus(200);
}
// Verifica scadenza
const now = new Date();
if (now > user.profile.telegram_verification_expires) {
await User.updateOne(
{ _id: user._id },
{
$unset: {
'profile.telegram_verification_token': '',
'profile.telegram_verification_expires': '',
},
}
);
await sendTelegramMessage(
chatId,
"⏰ Il token è scaduto. Richiedi un nuovo link dall'applicazione."
);
return res.sendStatus(200);
}
// Verifica se già collegato
if (user.profile?.teleg_id && user.profile?.username_telegram) {
await sendTelegramMessage(chatId, '✅ Questo account è già stato verificato!');
return res.sendStatus(200);
}
// Salva i dati Telegram e rimuovi il token
await User.updateOne(
{ _id: user._id },
{
$set: {
'profile.teleg_id': userId.toString(),
'profile.username_telegram': username || firstName,
},
$unset: {
'profile.telegram_verification_token': '',
'profile.telegram_verification_expires': '',
},
}
);
// Invia messaggio di conferma
await sendTelegramMessage(
chatId,
`✅ Account verificato con successo!\n\n` +
`Il tuo Telegram è stato collegato a: ${user.username}\n\n` +
`Puoi ora tornare all'applicazione.`
);
}
res.sendStatus(200);
} catch (error) {
console.error('Errore webhook Telegram:', error);
res.sendStatus(500);
}
};
/**
* Funzione helper per inviare messaggi Telegram
*/
async function sendTelegramMessage(chatId, text) {
try {
await axios.post(`${TELEGRAM_API_URL}/sendMessage`, {
chat_id: chatId,
text: text,
parse_mode: 'HTML',
});
} catch (error) {
console.error('Errore invio messaggio Telegram:', error);
}
}
/**
* Configura il webhook di Telegram (da eseguire una volta)
*/
exports.setupWebhook = async (req, res) => {
try {
/*const webhookUrl = `${process.env.APP_URL}/api/telegram/webhook`;
const response = await axios.post(`${TELEGRAM_API_URL}/setWebhook`, {
url: webhookUrl,
allowed_updates: ['message'],
});
res.json({
success: true,
message: 'Webhook configurato',
data: response.data,
});*/
} catch (error) {
console.error('Errore setup webhook:', error);
return res.status(500).json({ error: 'Errore configurazione webhook' });
}
};
/**
* Rimuovi collegamento Telegram
*/
exports.unlinkTelegram = async (req, res) => {
try {
const userId = req.user.id; // Assumi autenticazione middleware
await User.updateOne(
{ _id: userId },
{
$unset: {
'profile.teleg_id': '',
'profile.username_telegram': '',
'profile.telegram_verification_token': '',
'profile.telegram_verification_expires': '',
},
}
);
res.json({ success: true, message: 'Telegram scollegato' });
} catch (error) {
console.error('Errore unlink Telegram:', error);
return res.status(500).json({ error: 'Errore del server' });
}
};
/**
* Salta la verifica Telegram
*/
exports.skipTelegramVerification = async (req, res) => {
try {
const { idapp } = req.body;
const user = await User.findOne({ idapp });
if (!user) {
return res.status(404).json({ error: 'Utente non trovato' });
}
await User.updateOne(
{ _id: user._id },
{
$set: {
'profile.telegram_verification_skipped': true,
},
$unset: {
'profile.telegram_verification_token': '',
'profile.telegram_verification_expires': '',
},
}
);
res.json({ success: true, message: 'Verifica Telegram saltata' });
} catch (error) {
console.error('Errore skip verifica:', error);
return res.status(500).json({ error: 'Errore del server' });
}
};