diff --git a/.env.development b/.env.development
index 7d4430b..63d130b 100644
--- a/.env.development
+++ b/.env.development
@@ -39,3 +39,5 @@ AUTH_NEW_SITES=123123123
SCRIPTS_DIR=admin_scripts
CLOUDFLARE_TOKENS=[{"label":"Paolo.arena77@gmail.com","value":"M9EM309v8WFquJKpYgZCw-TViM2wX6vB3wlK6GD0"},{"label":"gruppomacro.com","value":"bqmzGShoX7WqOBzkXocoECyBkPq3GfqcM5t6VFd8"}]
DS_API_KEY="sk-222e3addb3d8455d8b0516d93906eec7"
+OLLAMA_URL=http://localhost:11434
+OLLAMA_DEFAULT_MODEL=llama3.2:3b
\ No newline at end of file
diff --git a/emails/defaultSite/reg_resend_email_to_verifiyng/it/html.pug b/emails/defaultSite/reg_resend_email_to_verifiyng/it/html.pug
new file mode 100755
index 0000000..e8492a9
--- /dev/null
+++ b/emails/defaultSite/reg_resend_email_to_verifiyng/it/html.pug
@@ -0,0 +1,404 @@
+doctype html
+html(lang="it")
+ head
+ meta(charset="UTF-8")
+ meta(name="viewport" content="width=device-width, initial-scale=1.0")
+ style(type="text/css").
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ background-color: #f5f5f5;
+ padding: 20px;
+ line-height: 1.6;
+ }
+
+ .header-logo {
+ width: 80px;
+ height: auto;
+ margin-bottom: 16px;
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ .email-container {
+ max-width: 600px;
+ margin: 0 auto;
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ }
+
+ .email-header {
+ background: linear-gradient(135deg, #1976D2 0%, #1565C0 100%);
+ color: white;
+ padding: 40px 24px;
+ text-align: center;
+ }
+
+ .email-header h1 {
+ margin: 0 0 8px 0;
+ font-size: 26px;
+ font-weight: 600;
+ }
+
+ .email-header p {
+ margin: 8px 0 0 0;
+ font-size: 16px;
+ opacity: 0.95;
+ line-height: 1.5;
+ }
+
+ .verification-icon {
+ font-size: 56px;
+ margin-bottom: 16px;
+ }
+
+ .email-body {
+ padding: 24px 20px;
+ }
+
+ .intro-text {
+ font-size: 16px;
+ color: #333;
+ margin-bottom: 20px;
+ text-align: center;
+ line-height: 1.7;
+ padding: 0 10px;
+ }
+
+ .intro-text strong {
+ color: #1976D2;
+ }
+
+ .info-message {
+ background: linear-gradient(135deg, #E3F2FD 0%, #BBDEFB 100%);
+ border-left: 4px solid #1976D2;
+ border-radius: 8px;
+ padding: 20px;
+ margin-bottom: 24px;
+ text-align: center;
+ }
+
+ .info-message h2 {
+ color: #1565C0;
+ font-size: 18px;
+ margin-bottom: 12px;
+ font-weight: 600;
+ }
+
+ .info-message p {
+ color: #1976D2;
+ font-size: 15px;
+ line-height: 1.6;
+ margin: 8px 0;
+ }
+
+ .warning-box {
+ background: #FFF8E1;
+ border-left: 4px solid #FFA000;
+ border-radius: 8px;
+ padding: 16px;
+ margin-bottom: 24px;
+ }
+
+ .warning-box p {
+ color: #E65100;
+ font-size: 14px;
+ margin: 0;
+ line-height: 1.6;
+ }
+
+ .warning-box strong {
+ color: #BF360C;
+ }
+
+ .email-info-box {
+ background: #f8f9fa;
+ border-left: 4px solid #1976D2;
+ border-radius: 8px;
+ padding: 16px;
+ margin-bottom: 24px;
+ text-align: center;
+ }
+
+ .email-info-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #555;
+ margin-bottom: 8px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ .email-address {
+ font-size: 18px;
+ font-weight: 600;
+ color: #1976D2;
+ word-break: break-word;
+ }
+
+ .cta-section {
+ margin: 24px 0;
+ padding: 24px 0;
+ text-align: center;
+ }
+
+ .cta-title {
+ font-size: 19px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin-bottom: 8px;
+ }
+
+ .cta-subtitle {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 20px;
+ line-height: 1.5;
+ }
+
+ .cta-button {
+ display: inline-block;
+ padding: 18px 40px;
+ font-size: 16px;
+ font-weight: 600;
+ color: white;
+ background: linear-gradient(135deg, #1976D2 0%, #1565C0 100%);
+ border-radius: 50px;
+ text-decoration: none;
+ box-shadow: 0 4px 12px rgba(25, 118, 210, 0.35);
+ transition: transform 0.2s, box-shadow 0.2s;
+ }
+
+ .cta-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(25, 118, 210, 0.45);
+ }
+
+ .button-icon {
+ font-size: 20px;
+ margin-right: 8px;
+ vertical-align: middle;
+ }
+
+ .link-fallback {
+ margin-top: 20px;
+ padding: 16px;
+ background: #f5f5f5;
+ border-radius: 8px;
+ text-align: center;
+ }
+
+ .link-fallback p {
+ font-size: 13px;
+ color: #666;
+ margin-bottom: 8px;
+ }
+
+ .link-fallback a {
+ font-size: 12px;
+ color: #1976D2;
+ word-break: break-all;
+ }
+
+ .expiry-notice {
+ background: linear-gradient(135deg, #FFEBEE 0%, #FFCDD2 100%);
+ border-radius: 8px;
+ padding: 16px;
+ margin: 24px 0;
+ text-align: center;
+ border: 1px solid #EF9A9A;
+ }
+
+ .expiry-notice p {
+ margin: 0;
+ color: #C62828;
+ font-size: 14px;
+ line-height: 1.6;
+ }
+
+ .expiry-notice strong {
+ color: #B71C1C;
+ }
+
+ .help-section {
+ background: #FAFAFA;
+ border-radius: 8px;
+ padding: 20px;
+ margin: 24px 0;
+ text-align: center;
+ }
+
+ .help-section h3 {
+ font-size: 16px;
+ color: #333;
+ margin-bottom: 12px;
+ font-weight: 600;
+ }
+
+ .help-section p {
+ font-size: 14px;
+ color: #666;
+ margin: 8px 0;
+ line-height: 1.6;
+ }
+
+ .help-section a {
+ color: #1976D2;
+ text-decoration: none;
+ }
+
+ .help-section a:hover {
+ text-decoration: underline;
+ }
+
+ .security-note {
+ background: #E8F5E9;
+ border-left: 4px solid #4CAF50;
+ border-radius: 8px;
+ padding: 14px;
+ margin: 20px 0;
+ }
+
+ .security-note p {
+ font-size: 13px;
+ color: #2E7D32;
+ margin: 0;
+ line-height: 1.5;
+ }
+
+ .email-footer {
+ padding: 20px 16px;
+ text-align: center;
+ background: #f8f9fa;
+ color: #777;
+ font-size: 13px;
+ }
+
+ .email-footer p {
+ margin: 6px 0;
+ }
+
+ .divider {
+ height: 1px;
+ background: linear-gradient(to right, transparent, #e0e0e0, transparent);
+ margin: 24px 0;
+ }
+
+ @media only screen and (max-width: 600px) {
+ body {
+ padding: 8px;
+ }
+
+ .email-header {
+ padding: 24px 16px;
+ }
+
+ .email-header h1 {
+ font-size: 22px;
+ }
+
+ .email-header p {
+ font-size: 15px;
+ }
+
+ .email-body {
+ padding: 20px 14px;
+ }
+
+ .info-message {
+ padding: 16px;
+ }
+
+ .email-address {
+ font-size: 16px;
+ }
+
+ .cta-button {
+ padding: 16px 32px;
+ font-size: 15px;
+ display: block;
+ width: 100%;
+ }
+
+ .link-fallback a {
+ font-size: 11px;
+ }
+ }
+
+ body
+ .email-container
+ .email-header
+ - var baseimg = baseurl + '/';
+ h1 ✉️ Verifica il tuo indirizzo email
+ p Conferma la tua email per continuare a usare #{nomeapp}
+
+ .email-body
+ .intro-text
+ | Ciao
+ strong #{name || username}
+ | ! 👋
+ br
+ | Abbiamo ricevuto una richiesta per verificare nuovamente il tuo indirizzo email.
+
+ .info-message
+ h2 📧 Perché devo verificare di nuovo?
+ p Potrebbe essere perché hai cambiato email, per motivi di sicurezza, o perché la verifica precedente è scaduta.
+ p Questa procedura ci aiuta a mantenere sicuro il tuo account.
+
+ if emailto
+ .email-info-box
+ .email-info-title Email da verificare
+ .email-address #{emailto}
+
+ .cta-section
+ .cta-title Conferma il tuo indirizzo email
+ .cta-subtitle Clicca il bottone qui sotto per completare la verifica
+
+ if verifyLink
+ a.cta-button(href=verifyLink target="_blank")
+ span.button-icon ✓
+ | Verifica Email
+
+ .link-fallback
+ p Se il bottone non funziona, copia e incolla questo link nel browser:
+ a(href=verifyLink) #{verifyLink}
+
+ .expiry-notice
+ p ⏰ Questo link scadrà tra
+ strong 24 ore
+ | .
+ p Se non completi la verifica in tempo, dovrai richiedere un nuovo link.
+
+ .warning-box
+ p ⚠️
+ strong Non hai richiesto questa verifica?
+ | Ignora questa email. Il tuo account resterà al sicuro.
+
+ .security-note
+ p 🔒 Per la tua sicurezza: non condividere mai questo link con nessuno. Il team di #{nomeapp} non ti chiederà mai la password via email.
+
+ .help-section
+ h3 Hai bisogno di aiuto?
+ p Non riesci a verificare la tua email?
+ if supportEmail
+ p Contattaci a
+ a(href="mailto:" + supportEmail) #{supportEmail}
+ if strlinksito
+ p Oppure visita la nostra
+ a(href=strlinksito + '/supporto' target="_blank") pagina di supporto
+
+ .email-footer
+ .divider
+ p Hai ricevuto questa email perché è stata richiesta una verifica per il tuo account su #{nomeapp}
+ p(style="margin-top: 8px; font-size: 12px; color: #999;")
+ | Se non hai fatto tu questa richiesta, puoi ignorare questa email.
+ p(style="margin-top: 12px; font-size: 12px;")
+ | © #{new Date().getFullYear()} #{nomeapp}
\ No newline at end of file
diff --git a/emails/defaultSite/reg_resend_email_to_verifiyng/it/subject.pug b/emails/defaultSite/reg_resend_email_to_verifiyng/it/subject.pug
new file mode 100755
index 0000000..6d8ccca
--- /dev/null
+++ b/emails/defaultSite/reg_resend_email_to_verifiyng/it/subject.pug
@@ -0,0 +1 @@
+Verifica la tua Email - ${nomeapp}`
diff --git a/logtrans.txt b/logtrans.txt
index f8b62ee..0438ab2 100644
--- a/logtrans.txt
+++ b/logtrans.txt
@@ -519,4 +519,19 @@ Gio 04/12 ORE 18:55: [Circuito RIS Bologna]: Inviate Monete da SurTest a
Saldi:
SurTest: 0.00 RIS]
-ElenaEspx: 38.05 RIS]
\ No newline at end of file
+ElenaEspx: 38.05 RIS]
+Mar 09/12 ORE 21:36: [Circuito RIS Venezia]: Inviate Monete da surya1977 a amandadi 11 RIS [causale: ssss]
+
+Saldi:
+surya1977: 63.00 RIS]
+amandadi: 12.00 RIS]
+Mer 10/12 ORE 16:18: [Circuito RIS Venezia]: Inviate Monete da surya1977 a amandadi 12 RIS [causale: Ciaoo]
+
+Saldi:
+surya1977: 51.00 RIS]
+amandadi: 24.00 RIS]
+Mer 10/12 ORE 17:02: [Circuito RIS Venezia]: Inviate Monete da surya1977 a amandadi 52 RIS [causale: Ancora test]
+
+Saldi:
+surya1977: -1.00 RIS]
+amandadi: 76.00 RIS]
\ No newline at end of file
diff --git a/package.json b/package.json
index a06babe..0f7467b 100755
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"email-templates": "^12.0.2",
"entities": "^7.0.0",
"express": "^4.21.2",
+ "express-rate-limit": "^7.1.5",
"fast-csv": "^5.0.5",
"formidable": "^3.5.2",
"fs-extra": "^11.3.2",
diff --git a/src/config/config.js b/src/config/config.js
index 7444744..f65d10b 100755
--- a/src/config/config.js
+++ b/src/config/config.js
@@ -25,6 +25,7 @@ var file = `.env.${node_env}`;
// GLOBALI (Uguali per TUTTI)
process.env.LINKVERIF_REG = '/vreg';
+process.env.CHECKREVERIF_EMAIL = '/reverif_email';
process.env.LINK_REQUEST_NEWPASSWORD = '/requestnewpwd';
process.env.ADD_NEW_SITE = '/addNewSite';
process.env.LINK_UPDATE_PASSWORD = '/updatepassword';
diff --git a/src/models/movement.js b/src/models/movement.js
index 02747a4..c1eadf7 100755
--- a/src/models/movement.js
+++ b/src/models/movement.js
@@ -342,8 +342,12 @@ MovementSchema.statics.getQueryMovsByCircuitId = async function (idapp, username
'circuitfrom.symbol': 1,
'circuitto.symbol': 1,
'userfrom.verified_by_aportador': 1,
+ 'userfrom.name': 1,
+ 'userfrom.surname': 1,
'userfrom.username': 1,
'userfrom.profile.img': 1,
+ 'userto.name': 1,
+ 'userto.surname': 1,
'userto.username': 1,
'userto.profile.img': 1,
'userto.verified_by_aportador': 1,
@@ -579,7 +583,11 @@ MovementSchema.statics.getQueryAllUsersMovsByCircuitId = async function (idapp,
'circuitfrom.symbol': 1,
'circuitto.symbol': 1,
'userfrom.username': 1,
+ 'userfrom.name': 1,
+ 'userfrom.surname': 1,
'userfrom.profile.img': 1,
+ 'userto.name': 1,
+ 'userto.surname': 1,
'userto.username': 1,
'userto.profile.img': 1,
'groupfrom.groupname': 1,
@@ -1013,7 +1021,11 @@ MovementSchema.statics.getLastN_Transactions = async function (
'circuitto.name': 1,
'userfrom.verified_by_aportador': 1,
'userfrom.username': 1,
+ 'userfrom.name': 1,
+ 'userfrom.surname': 1,
'userfrom.profile.img': 1,
+ 'userto.name': 1,
+ 'userto.surname': 1,
'userto.username': 1,
'userto.profile.img': 1,
'userto.verified_by_aportador': 1,
diff --git a/src/models/user.js b/src/models/user.js
index 580c16f..8d6924e 100755
--- a/src/models/user.js
+++ b/src/models/user.js
@@ -56,6 +56,9 @@ const UserSchema = new mongoose.Schema(
message: '{VALUE} is not a valid email'
}*/
},
+ link_verif_email: {
+ type: String,
+ },
hash: {
type: String,
},
@@ -2614,6 +2617,12 @@ UserSchema.statics.removeBookmark = async function (idapp, username, id, tab) {
UserSchema.statics.addBookmark = async function (idapp, username, id, tab) {
return await User.updateOne({ idapp, username }, { $push: { 'profile.bookmark': { id, tab } } });
};
+UserSchema.statics.setLinkToVerifiedEmail = async function (idapp, username, link_verif_email) {
+ return await User.updateOne({ idapp, username }, { $set: { link_verif_email } });
+};
+UserSchema.statics.findByLinkVerifEmail = async function (idapp, link_verif_email) {
+ return await User.findOne({ idapp, link_verif_email });
+};
// Rimuovo il Partecipa
UserSchema.statics.removeAttend = async function (idapp, username, id, tab) {
return await User.updateOne({ idapp, username }, { $pull: { 'profile.attend': { id: { $in: [id] }, tab } } });
@@ -6989,28 +6998,24 @@ UserSchema.statics.getTokenByUsernameAndCircuitName = async function (idapp, use
return user?.profile?.mycircuits?.[0]?.token || null;
};
-UserSchema.statics.softDelete = async function(id) {
+UserSchema.statics.softDelete = async function (id) {
return this.findByIdAndUpdate(
id,
- {
+ {
deleted: true,
- deletedAt: new Date()
+ deletedAt: new Date(),
},
{ new: true }
);
};
-UserSchema.statics.getUsersList = function(idapp) {
+UserSchema.statics.getUsersList = function (idapp) {
return this.find({
idapp: idapp,
- $or: [
- { deleted: { $exists: false } },
- { deleted: false }
- ]
+ $or: [{ deleted: { $exists: false } }, { deleted: false }],
}).lean();
};
-
const User = mongoose.model('User', UserSchema);
class Hero {
diff --git a/src/populate/populate.js b/src/populate/populate.js
index eccd613..561122f 100644
--- a/src/populate/populate.js
+++ b/src/populate/populate.js
@@ -25,6 +25,7 @@ module.exports = {
});
}
}
+
} catch (e) {
console.log('error insertIntoDb', e);
}
diff --git a/src/router/api2_router.js b/src/router/api2_router.js
new file mode 100644
index 0000000..ba02750
--- /dev/null
+++ b/src/router/api2_router.js
@@ -0,0 +1,913 @@
+/**
+ * API2 Router - Ollama Integration
+ */
+
+const express = require('express');
+
+const router = express.Router();
+
+// Configurazione Ollama
+const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434';
+const DEFAULT_MODEL = process.env.OLLAMA_DEFAULT_MODEL || 'llama3.2';
+
+// ============================================
+// ROUTES PRINCIPALI
+// ============================================
+
+/**
+ * Health check Ollama
+ * GET /api2/health
+ */
+router.get('/health', async (req, res) => {
+ try {
+ const response = await fetch(`${OLLAMA_URL}/api/tags`);
+ if (response.ok) {
+ const data = await response.json();
+ res.json({
+ status: 'ok',
+ ollama: 'connected',
+ modelsCount: data.models?.length || 0
+ });
+ } else {
+ res.status(503).json({ status: 'error', ollama: 'disconnected' });
+ }
+ } catch (error) {
+ res.status(503).json({
+ status: 'error',
+ ollama: 'unreachable',
+ message: error.message
+ });
+ }
+});
+
+/**
+ * Lista modelli disponibili
+ * GET /api2/models
+ */
+router.get('/models', async (req, res) => {
+ try {
+
+ const response = await fetch(`${OLLAMA_URL}/api/tags`);
+ if (!response.ok) {
+ console.error('[api2/models] Error:', response.status);
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+ const data = await response.json();
+ res.json({ models: data.models || [] });
+ } catch (error) {
+ console.error('[api2/models] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Info modello specifico
+ * POST /api2/models/info
+ */
+router.post('/models/info', async (req, res) => {
+ try {
+ const { model } = req.body;
+ if (!model) {
+ return res.status(400).json({ error: 'Model name richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/show`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ name: model }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json(data);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// ============================================
+// GENERAZIONE TESTO
+// ============================================
+
+/**
+ * Generazione testo (non-streaming)
+ * POST /api2/generate
+ */
+router.post('/generate', async (req, res) => {
+ try {
+ const {
+ model = DEFAULT_MODEL,
+ prompt,
+ temperature = 0.7,
+ maxTokens,
+ system,
+ topP,
+ topK,
+ } = req.body;
+
+ if (!prompt) {
+ return res.status(400).json({ error: 'Prompt richiesto' });
+ }
+
+ const payload = {
+ model,
+ prompt,
+ stream: false,
+ options: {
+ temperature,
+ ...(maxTokens && { num_predict: maxTokens }),
+ ...(topP && { top_p: topP }),
+ ...(topK && { top_k: topK }),
+ },
+ };
+
+ if (system) payload.system = system;
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ response: data.response,
+ model: data.model,
+ totalDuration: data.total_duration,
+ loadDuration: data.load_duration,
+ promptEvalCount: data.prompt_eval_count,
+ evalCount: data.eval_count,
+ });
+ } catch (error) {
+ console.error('[api2/generate] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Generazione testo con streaming (SSE)
+ * POST /api2/generate/stream
+ */
+router.post('/generate/stream', async (req, res) => {
+ try {
+ const {
+ model = DEFAULT_MODEL,
+ prompt,
+ temperature = 0.7,
+ system,
+ maxTokens,
+ } = req.body;
+
+ if (!prompt) {
+ return res.status(400).json({ error: 'Prompt richiesto' });
+ }
+
+ const payload = {
+ model,
+ prompt,
+ stream: true,
+ options: {
+ temperature,
+ ...(maxTokens && { num_predict: maxTokens }),
+ },
+ };
+
+ if (system) payload.system = system;
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ // Setup SSE
+ res.setHeader('Content-Type', 'text/event-stream');
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Connection', 'keep-alive');
+ res.setHeader('X-Accel-Buffering', 'no');
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+
+ const pump = async () => {
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) {
+ res.write('data: [DONE]\n\n');
+ res.end();
+ break;
+ }
+
+ const chunk = decoder.decode(value);
+ const lines = chunk.split('\n').filter(line => line.trim());
+
+ for (const line of lines) {
+ try {
+ const json = JSON.parse(line);
+ res.write(`data: ${JSON.stringify(json)}\n\n`);
+ } catch (e) {
+ // Ignora linee non JSON
+ }
+ }
+ }
+ } catch (error) {
+ console.error('[api2/generate/stream] Stream error:', error);
+ res.end();
+ }
+ };
+
+ // Handle client disconnect
+ req.on('close', () => {
+ reader.cancel();
+ });
+
+ pump();
+
+ } catch (error) {
+ console.error('[api2/generate/stream] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// ============================================
+// CHAT
+// ============================================
+
+/**
+ * Chat (non-streaming)
+ * POST /api2/chat
+ */
+router.post('/chat', async (req, res) => {
+ try {
+ const {
+ model = DEFAULT_MODEL,
+ messages,
+ temperature = 0.7,
+ system,
+ maxTokens,
+ } = req.body;
+
+ if (!messages || !Array.isArray(messages)) {
+ return res.status(400).json({ error: 'Messages array richiesto' });
+ }
+
+ const payload = {
+ model,
+ messages,
+ stream: false,
+ options: {
+ temperature,
+ ...(maxTokens && { num_predict: maxTokens }),
+ },
+ };
+
+ if (system) payload.system = system;
+
+ // console.log('payload', payload);
+
+ const response = await fetch(`${OLLAMA_URL}/api/chat`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status} - ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ message: data.message,
+ model: data.model,
+ totalDuration: data.total_duration,
+ });
+ } catch (error) {
+ console.error('[api2/chat] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Chat con streaming (SSE)
+ * POST /api2/chat/stream
+ */
+router.post('/chat/stream', async (req, res) => {
+ try {
+ const {
+ model = DEFAULT_MODEL,
+ messages,
+ temperature = 0.7,
+ system,
+ maxTokens,
+ } = req.body;
+
+ if (!messages || !Array.isArray(messages)) {
+ return res.status(400).json({ error: 'Messages array richiesto' });
+ }
+
+ const payload = {
+ model,
+ messages,
+ stream: true,
+ options: {
+ temperature,
+ ...(maxTokens && { num_predict: maxTokens }),
+ },
+ };
+
+ if (system) payload.system = system;
+
+ const response = await fetch(`${OLLAMA_URL}/api/chat`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ // Setup SSE
+ res.setHeader('Content-Type', 'text/event-stream');
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Connection', 'keep-alive');
+ res.setHeader('X-Accel-Buffering', 'no');
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+
+ const pump = async () => {
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) {
+ res.write('data: [DONE]\n\n');
+ res.end();
+ break;
+ }
+
+ const chunk = decoder.decode(value);
+ const lines = chunk.split('\n').filter(line => line.trim());
+
+ for (const line of lines) {
+ try {
+ const json = JSON.parse(line);
+ res.write(`data: ${JSON.stringify(json)}\n\n`);
+ } catch (e) {
+ // Ignora linee non JSON
+ }
+ }
+ }
+ } catch (error) {
+ console.error('[api2/chat/stream] Stream error:', error);
+ res.end();
+ }
+ };
+
+ // Handle client disconnect
+ req.on('close', () => {
+ reader.cancel();
+ });
+
+ pump();
+
+ } catch (error) {
+ console.error('[api2/chat/stream] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// ============================================
+// ENDPOINTS HELPER
+// ============================================
+
+/**
+ * Genera codice
+ * POST /api2/code
+ */
+router.post('/code', async (req, res) => {
+ try {
+ const {
+ prompt,
+ language = 'javascript',
+ model = DEFAULT_MODEL,
+ temperature = 0.3,
+ } = req.body;
+
+ if (!prompt) {
+ return res.status(400).json({ error: 'Prompt richiesto' });
+ }
+
+ const systemPrompt = `Sei un esperto programmatore. Rispondi SOLO con codice ${language} valido e funzionante, senza spiegazioni prima o dopo. Usa commenti nel codice se necessario per spiegare.`;
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Scrivi codice ${language} per: ${prompt}`,
+ system: systemPrompt,
+ stream: false,
+ options: { temperature },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ code: data.response,
+ language,
+ model: data.model,
+ });
+ } catch (error) {
+ console.error('[api2/code] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Traduci testo
+ * POST /api/translate
+ */
+router.post('/translate', async (req, res) => {
+ try {
+ const {
+ text,
+ targetLang = 'english',
+ sourceLang,
+ model = DEFAULT_MODEL,
+ } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+
+ const sourceInfo = sourceLang ? ` da ${sourceLang}` : '';
+ const prompt = `Traduci il seguente testo${sourceInfo} in ${targetLang}. Rispondi SOLO con la traduzione, senza spiegazioni:\n\n${text}`;
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt,
+ stream: false,
+ options: { temperature: 0.3 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ translation: data.response.trim(),
+ targetLang,
+ sourceLang,
+ });
+ } catch (error) {
+ console.error('[api2/translate] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Riassumi testo
+ * POST /api/summarize
+ */
+router.post('/summarize', async (req, res) => {
+ try {
+ const {
+ text,
+ maxLength,
+ style = 'conciso', // conciso, dettagliato, bullet
+ model = DEFAULT_MODEL,
+ } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+
+ let styleInstruction = '';
+ switch (style) {
+ case 'bullet':
+ styleInstruction = 'Usa un elenco puntato.';
+ break;
+ case 'dettagliato':
+ styleInstruction = 'Fornisci un riassunto dettagliato.';
+ break;
+ default:
+ styleInstruction = 'Sii conciso e chiaro.';
+ }
+
+ const lengthInstruction = maxLength ? ` Massimo ${maxLength} parole.` : '';
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Riassumi il seguente testo. ${styleInstruction}${lengthInstruction}\n\n${text}`,
+ stream: false,
+ options: { temperature: 0.5 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ summary: data.response.trim(),
+ style,
+ });
+ } catch (error) {
+ console.error('[api2/summarize] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Analisi sentiment
+ * POST /api/sentiment
+ */
+router.post('/sentiment', async (req, res) => {
+ try {
+ const { text, model = DEFAULT_MODEL } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Analizza il sentiment del seguente testo e rispondi SOLO con un JSON valido nel formato:
+{"sentiment": "positive" | "negative" | "neutral" | "mixed", "confidence": 0.0-1.0, "emotions": ["emotion1", "emotion2"], "explanation": "breve spiegazione"}
+
+Testo da analizzare:
+${text}`,
+ stream: false,
+ options: { temperature: 0.1 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ try {
+ const jsonMatch = data.response.match(/\{[\s\S]*\}/);
+ if (jsonMatch) {
+ const parsed = JSON.parse(jsonMatch[0]);
+ res.json(parsed);
+ } else {
+ res.json({ sentiment: 'unknown', raw: data.response });
+ }
+ } catch (e) {
+ res.json({ sentiment: 'unknown', raw: data.response });
+ }
+ } catch (error) {
+ console.error('[api2/sentiment] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Estrai JSON strutturato da testo
+ * POST /api/extract
+ */
+router.post('/extract', async (req, res) => {
+ try {
+ const { text, schema, model = DEFAULT_MODEL } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+ if (!schema) {
+ return res.status(400).json({ error: 'Schema richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Estrai le informazioni dal seguente testo e restituisci SOLO un JSON valido con questa struttura:
+${JSON.stringify(schema, null, 2)}
+
+Testo da analizzare:
+${text}
+
+Rispondi SOLO con il JSON, senza altro testo.`,
+ stream: false,
+ options: { temperature: 0.1 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ try {
+ const jsonMatch = data.response.match(/\{[\s\S]*\}/);
+ if (jsonMatch) {
+ res.json(JSON.parse(jsonMatch[0]));
+ } else {
+ res.status(422).json({ error: 'Could not parse JSON', raw: data.response });
+ }
+ } catch (e) {
+ res.status(422).json({ error: 'Invalid JSON response', raw: data.response });
+ }
+ } catch (error) {
+ console.error('[api2/extract] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Correggi grammatica
+ * POST /api/grammar
+ */
+router.post('/grammar', async (req, res) => {
+ try {
+ const {
+ text,
+ language = 'italiano',
+ model = DEFAULT_MODEL,
+ } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Correggi gli errori grammaticali e di ortografia nel seguente testo in ${language}. Rispondi SOLO con il testo corretto, senza spiegazioni:\n\n${text}`,
+ stream: false,
+ options: { temperature: 0.2 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ corrected: data.response.trim(),
+ original: text,
+ language,
+ });
+ } catch (error) {
+ console.error('[api2/grammar] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Rispondi a domanda con contesto
+ * POST /api/qa
+ */
+router.post('/qa', async (req, res) => {
+ try {
+ const {
+ question,
+ context,
+ model = DEFAULT_MODEL,
+ } = req.body;
+
+ if (!question) {
+ return res.status(400).json({ error: 'Question richiesta' });
+ }
+ if (!context) {
+ return res.status(400).json({ error: 'Context richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Basandoti SOLO sul seguente contesto, rispondi alla domanda. Se la risposta non è nel contesto, dì che non hai abbastanza informazioni.
+
+Contesto:
+${context}
+
+Domanda: ${question}
+
+Risposta:`,
+ stream: false,
+ options: { temperature: 0.3 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ answer: data.response.trim(),
+ question,
+ });
+ } catch (error) {
+ console.error('[api2/qa] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Genera embeddings
+ * POST /api/embeddings
+ */
+router.post('/embeddings', async (req, res) => {
+ try {
+ const {
+ text,
+ model = 'nomic-embed-text',
+ } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/embeddings`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: text,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+ res.json({
+ embedding: data.embedding,
+ dimensions: data.embedding?.length,
+ model,
+ });
+ } catch (error) {
+ console.error('[api2/embeddings] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Classifica testo in categorie
+ * POST /api/classify
+ */
+router.post('/classify', async (req, res) => {
+ try {
+ const {
+ text,
+ categories,
+ model = DEFAULT_MODEL,
+ } = req.body;
+
+ if (!text) {
+ return res.status(400).json({ error: 'Text richiesto' });
+ }
+ if (!categories || !Array.isArray(categories)) {
+ return res.status(400).json({ error: 'Categories array richiesto' });
+ }
+
+ const response = await fetch(`${OLLAMA_URL}/api/generate`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ model,
+ prompt: `Classifica il seguente testo in UNA delle seguenti categorie: ${categories.join(', ')}
+
+Rispondi SOLO con un JSON nel formato:
+{"category": "categoria_scelta", "confidence": 0.0-1.0, "reason": "breve motivazione"}
+
+Testo: ${text}`,
+ stream: false,
+ options: { temperature: 0.1 },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Ollama error: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ try {
+ const jsonMatch = data.response.match(/\{[\s\S]*\}/);
+ if (jsonMatch) {
+ res.json(JSON.parse(jsonMatch[0]));
+ } else {
+ res.json({ category: 'unknown', raw: data.response });
+ }
+ } catch (e) {
+ res.json({ category: 'unknown', raw: data.response });
+ }
+ } catch (error) {
+ console.error('[api2/classify] Error:', error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+
+/**
+ * Pull/scarica un nuovo modello
+ * POST /api2/models/pull
+ */
+router.post('/models/pull', async (req, res) => {
+ try {
+ const { model } = req.body;
+ if (!model) {
+ return res.status(400).json({ error: 'Model name richiesto' });
+ }
+
+ // Streaming del progresso
+ res.setHeader('Content-Type', 'text/event-stream');
+ res.setHeader('Cache-Control', 'no-cache');
+ res.setHeader('Connection', 'keep-alive');
+
+ const response = await fetch(`${OLLAMA_URL}/api/pull`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ name: model, stream: true }),
+ });
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) {
+ res.write('data: [DONE]\n\n');
+ res.end();
+ break;
+ }
+
+ const chunk = decoder.decode(value);
+ res.write(`data: ${chunk}\n\n`);
+ }
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+});
+
+/**
+ * Elimina un modello
+ * DELETE /api2/models/:name
+ */
+router.delete('/models/:name', async (req, res) => {
+ try {
+ const { name } = req.params;
+
+ const response = await fetch(`${OLLAMA_URL}/api/delete`, {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ name }),
+ });
+
+ if (response.ok) {
+ res.json({ success: true, message: `Modello ${name} eliminato` });
+ } else {
+ throw new Error(`Errore eliminazione: ${response.status}`);
+ }
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// ============================================
+// EXPORT
+// ============================================
+
+module.exports = router;
\ No newline at end of file
diff --git a/src/router/index_router.js b/src/router/index_router.js
index 7aa5047..ae23f80 100755
--- a/src/router/index_router.js
+++ b/src/router/index_router.js
@@ -300,6 +300,44 @@ router.post(process.env.LINKVERIF_REG, (req, res) => {
});
});
+router.post(process.env.CHECKREVERIF_EMAIL, (req, res) => {
+ const body = _.pick(req.body, ['idapp', 'idlink']);
+ const idapp = body.idapp;
+ const idlink = body.idlink;
+
+ // Cerco l'idlink se è ancora da Verificare
+
+ User.findByLinkVerifEmail(idapp, idlink)
+ .then((user) => {
+ if (!user) {
+ //console.log("NON TROVATO!");
+ return res.status(404).send({code: RIS_CODE_ERRORE, msg: 'Verifica email non andata a buon fine. Ripetere.'});
+ } else {
+ console.log('user', user);
+ if (user.verified_email) {
+ res.send({
+ code: server_constants.RIS_CODE_EMAIL_ALREADY_VERIFIED,
+ msg: tools.getres__("L'Email è già stata Verificata", res),
+ });
+ } else {
+ user.verified_email = true;
+ user.lasttimeonline = new Date();
+ user.save().then(() => {
+ //console.log("TROVATOOOOOO!");
+ res.send({
+ code: server_constants.RIS_CODE_EMAIL_VERIFIED,
+ msg: tools.getres__('EMAIL', res) + ' ' + tools.getres__('VERIF', res),
+ });
+ });
+ }
+ }
+ })
+ .catch((e) => {
+ console.log(process.env.LINKVERIF_REG, e.message);
+ res.status(400).send();
+ });
+});
+
router.post(process.env.ADD_NEW_SITE, async (req, res) => {
try {
const body = req.body;
diff --git a/src/router/users_router.js b/src/router/users_router.js
index 8dc05d8..a86d8ca 100755
--- a/src/router/users_router.js
+++ b/src/router/users_router.js
@@ -506,13 +506,15 @@ router.post('/profile', authenticate, (req, res) => {
const perm = req.user ? req.user.perm : tools.Perm.PERM_NONE;
const username = req.body['username'];
const idapp = req.body.idapp;
+ const idnotif = req.body['idnotif'] || '';
//++Todo: controlla che tipo di dati ha il permesso di leggere
try {
// Check if ìs a Notif to read
- const idnotif = req.body['idnotif'] ? req.body['idnotif'] : '';
- SendNotif.setNotifAsRead(idapp, usernameOrig, idnotif);
+ if (idnotif) {
+ SendNotif.setNotifAsRead(idapp, usernameOrig, idnotif);
+ }
return User.getUserProfileByUsername(idapp, username, usernameOrig, false, perm)
.then((ris) => {
@@ -601,6 +603,7 @@ router.post('/panel', authenticate, async (req, res) => {
username: 1,
name: 1,
surname: 1,
+ verified_email: 1,
email: 1,
verified_by_aportador: 1,
aportador_solidario: 1,
@@ -1124,6 +1127,11 @@ async function eseguiDbOpUser(idapp, mydata, locale, req, res) {
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noCircuit': mydata.value } });
} else if (mydata.dbop === 'noComune') {
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noComune': mydata.value } });
+ } else if (mydata.dbop === 'verifiedemail') {
+ await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'verified_email': mydata.value } });
+ } else if (mydata.dbop === 'resendVerificationEmail') {
+ // Invia la email di Verifica email
+ const ris = await sendemail.sendEmail_ReVerifyingEmail(mydata, idapp);
} else if (mydata.dbop === 'noCircIta') {
await User.findOneAndUpdate({ _id: mydata._id }, { $set: { 'profile.noCircIta': mydata.value } });
} else if (mydata.dbop === 'insert_circuito_ita') {
diff --git a/src/sendemail.js b/src/sendemail.js
index 73107b6..22ef96f 100755
--- a/src/sendemail.js
+++ b/src/sendemail.js
@@ -494,6 +494,21 @@ module.exports = {
const strlinkreg = tools.getHostByIdApp(idapp) + process.env.LINKVERIF_REG + `/?idapp=${idapp}&idlink=${idreg}`;
return strlinkreg;
},
+ getlinkVerifyEmail: async function (idapp, email, username) {
+ try {
+ const reg = require('./reg/registration');
+
+
+ const idverif = reg.getlinkregByEmail(idapp, email, username);
+
+ await User.setLinkToVerifiedEmail(idapp, username, idverif);
+
+ const strlinkreg = tools.getHostByIdApp(idapp) + process.env.CHECKREVERIF_EMAIL + `/?idapp=${idapp}&idlink=${idverif}`;
+ return strlinkreg;
+ } catch (e) {
+ console.error('ERROR getlinkVerifyEmail');
+ }
+ },
getlinkInvitoReg: function (idapp, dati) {
const strlinkreg = tools.getHostByIdApp(idapp) + `/invitetoreg/${dati.token}`;
return strlinkreg;
@@ -675,6 +690,38 @@ module.exports = {
console.error('Err sendEmail_Utente_Ammesso', e);
}
},
+ sendEmail_ReVerifyingEmail: async function (dati, idapp) {
+ try {
+ const user = await User.getUserById(idapp, dati._id);
+ const lang = user.lang;
+ const username = user.username;
+ const email = user.email;
+
+ let mylocalsconf = {
+ idapp,
+ dataemail: await this.getdataemail(idapp),
+ baseurl: tools.getHostByIdApp(idapp),
+ locale: lang,
+ nomeapp: tools.getNomeAppByIdApp(idapp),
+ strlinksito: tools.getHostByIdApp(idapp),
+ //strlinkreg: this.getlinkReg(idapp, idreg),
+ emailto: email,
+ name: user.name,
+ username: user.username,
+ verifyLink: await this.getlinkVerifyEmail(idapp, email, username),
+ user,
+ supportEmail: tools.getContactEmailSupportBydApp(idapp),
+ };
+
+ const quale_email_inviare = this.getPathEmail(idapp, 'reg_resend_email_to_verifiyng') + '/' + lang;
+
+ const ris = await this.sendEmail_base(quale_email_inviare, email, mylocalsconf, '');
+
+ return ris;
+ } catch (e) {
+ console.error('Err sendEmail_ReVerifyingEmail', e);
+ }
+ },
sendEmail_Utente_Abilitato_Circuito_FidoConcesso: async function (lang, emailto, user, idapp, dati) {
try {
let mylocalsconf = {
diff --git a/src/server/setupRouters.js b/src/server/setupRouters.js
index 7dcb77f..390728d 100644
--- a/src/server/setupRouters.js
+++ b/src/server/setupRouters.js
@@ -36,6 +36,7 @@ function setupRouters(app) {
['/aitools', 'aitools_router'],
['/apisqlsrv', 'articleRoutes'],
['/api', 'api_router'],
+ ['/api2', 'api2_router'],
['/api/telegram', 'telegram_router'],
['/inviti', 'invitaAmicoRoutes'],
];
diff --git a/src/server_old.js b/src/server_old.js
index d85c2f8..bac849b 100755
--- a/src/server_old.js
+++ b/src/server_old.js
@@ -146,6 +146,7 @@ connectToDatabase(connectionUrl, options)
const aitools_router = require('./router/aitools_router');
const article_router = require('./router/articleRoutes');
const api_router = require('./router/api_router');
+ const api2_router = require('./router/api2_router');
const { MyEvent } = require('./models/myevent');
@@ -252,6 +253,7 @@ connectToDatabase(connectionUrl, options)
app.use('/aitools', aitools_router);
app.use('/apisqlsrv', article_router);
app.use('/api', api_router);
+ app.use('/api2', api2_router);
mystart();
});
diff --git a/src/tools/general.js b/src/tools/general.js
index 4f9a599..61e12d2 100755
--- a/src/tools/general.js
+++ b/src/tools/general.js
@@ -2023,6 +2023,19 @@ module.exports = {
return false;
},
+ getContactEmailSupportBydApp: function (idapp, option) {
+ const myapp = this.MYAPPS.find((item) => item.idapp === idapp);
+ if (myapp) {
+ if (myapp.hasOwnProperty('contacts')) {
+ if (myapp.confsite.hasOwnProperty('email')) {
+ return myapp.contacts.email;
+ }
+ }
+ }
+
+ return '';
+ },
+
getEnableTokenExpiredByIdApp: function (idapp) {
const myapp = this.MYAPPS.find((item) => item.idapp === idapp);
if (myapp && myapp.confpages && myapp.confpages.hasOwnProperty('enableTokenExpired')) {
@@ -5849,6 +5862,10 @@ module.exports = {
let mystr = '';
let userfrom = '';
let userto = '';
+ let namefrom = '';
+ let surnamefrom = '';
+ let nameto = '';
+ let surnameto = '';
let profilefrom = null;
let profileto = null;
@@ -5866,6 +5883,8 @@ module.exports = {
}
if (mov.userfrom) {
userfrom += mov.userfrom.username;
+ namefrom = mov.userfrom.name;
+ surnamefrom = mov.userfrom.surname;
profilefrom = mov.userfrom.profile;
}
@@ -5879,14 +5898,16 @@ module.exports = {
}
if (mov.userto) {
userto += mov.userto.username;
+ nameto = mov.userto.name;
+ surnameto = mov.userto.surname;
profileto = mov.userto.profile;
}
// mystr = t('movement.from') + userfrom + ' ' + t('movement.to') + userto
return {
- userfrom: { profile: profilefrom, username: userfrom },
- userto: { profile: profileto, username: userto },
+ userfrom: { profile: profilefrom, username: userfrom, name: namefrom, surname: surnamefrom },
+ userto: { profile: profileto, username: userto, name: nameto, surname: surnameto },
tipocontofrom,
tipocontoto,
};