- verifica email se non è stata verificata (componente)
- altri aggiornamenti grafica PAGERIS. - OLLAMA AI
This commit is contained in:
@@ -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
|
||||
404
emails/defaultSite/reg_resend_email_to_verifiyng/it/html.pug
Executable file
404
emails/defaultSite/reg_resend_email_to_verifiyng/it/html.pug
Executable file
@@ -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}
|
||||
1
emails/defaultSite/reg_resend_email_to_verifiyng/it/subject.pug
Executable file
1
emails/defaultSite/reg_resend_email_to_verifiyng/it/subject.pug
Executable file
@@ -0,0 +1 @@
|
||||
Verifica la tua Email - ${nomeapp}`
|
||||
17
logtrans.txt
17
logtrans.txt
@@ -519,4 +519,19 @@ Gio 04/12 ORE 18:55: [<b>Circuito RIS Bologna</b>]: Inviate Monete da SurTest a
|
||||
|
||||
Saldi:
|
||||
SurTest: 0.00 RIS]
|
||||
ElenaEspx: 38.05 RIS]
|
||||
ElenaEspx: 38.05 RIS]
|
||||
Mar 09/12 ORE 21:36: [<b>Circuito RIS Venezia</b>]: 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: [<b>Circuito RIS Venezia</b>]: 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: [<b>Circuito RIS Venezia</b>]: Inviate Monete da surya1977 a amandadi 52 RIS [causale: Ancora test]
|
||||
|
||||
Saldi:
|
||||
surya1977: -1.00 RIS]
|
||||
amandadi: 76.00 RIS]
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,6 +25,7 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log('error insertIntoDb', e);
|
||||
}
|
||||
|
||||
913
src/router/api2_router.js
Normal file
913
src/router/api2_router.js
Normal file
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -36,6 +36,7 @@ function setupRouters(app) {
|
||||
['/aitools', 'aitools_router'],
|
||||
['/apisqlsrv', 'articleRoutes'],
|
||||
['/api', 'api_router'],
|
||||
['/api2', 'api2_router'],
|
||||
['/api/telegram', 'telegram_router'],
|
||||
['/inviti', 'invitaAmicoRoutes'],
|
||||
];
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user