feat: script per criptare frasi motivazionali/demotivazionali e spostate all'interno del sqllite. Mantenuti file txt per compatibilità. Aggiunta soglia di attivazione nel env

This commit is contained in:
2026-07-08 03:09:44 +02:00
parent e6c3a6a43f
commit d39c9895b4
7 changed files with 208 additions and 13 deletions
+90
View File
@@ -1,6 +1,75 @@
const express = require('express');
const router = express.Router();
const pool = require('../db');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { db } = require('../activityDb');
const ALGORITHM = 'aes-256-cbc';
const SECRET_KEY = crypto.createHash('sha256').update(process.env.CRYPTO_KEY || 'default_secret_key_12345').digest();
const IV_LENGTH = 16;
function encrypt(text) {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
}
function decrypt(text) {
const textParts = text.split(':');
const iv = Buffer.from(textParts.shift(), 'hex');
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, SECRET_KEY, iv);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Ensure SQLite table exists for phrases
db.exec(`
CREATE TABLE IF NOT EXISTS frasi_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tipo TEXT,
testo TEXT
)
`);
// Seed function to import plain-text files into SQLite cache
function seedPhrases() {
try {
const countRow = db.prepare("SELECT COUNT(*) AS count FROM frasi_cache").get();
if (countRow.count === 0) {
console.log('[Phrases Seed] SQLite frasi_cache is empty. Seeding...');
const seedFile = (fileName, type) => {
const txtPath = path.join(__dirname, `../public/${fileName}`);
if (fs.existsSync(txtPath)) {
const text = fs.readFileSync(txtPath, 'utf8');
const lines = text.split('\n').map(line => line.trim()).filter(line => line.length > 0);
const insertStmt = db.prepare("INSERT INTO frasi_cache (tipo, testo) VALUES (?, ?)");
db.transaction(() => {
for (const line of lines) {
insertStmt.run(type, encrypt(line));
}
})();
console.log(`[Phrases Seed] Successfully seeded ${lines.length} encrypted ${type} phrases.`);
}
};
seedFile('demotivational.txt', 'demotivational');
seedFile('motivational.txt', 'motivational');
}
} catch (err) {
console.error('[Phrases Seed] Seeding failed:', err.message);
}
}
seedPhrases();
// GET /api/dashboard/stats — Dashboard statistics
router.get('/stats', async (req, res) => {
@@ -116,4 +185,25 @@ router.get('/stats', async (req, res) => {
}
});
// GET /api/dashboard/phrases — Get decrypted phrases from SQLite cache
router.get('/phrases', (req, res) => {
const { tipo = 'demotivational' } = req.query;
try {
const rows = db.prepare("SELECT testo FROM frasi_cache WHERE tipo = ?").all(tipo);
const decryptedPhrases = rows.map(row => {
try {
return decrypt(row.testo);
} catch (decErr) {
console.warn('[Decrypt Phrase] Failed to decrypt phrase:', decErr.message);
return null;
}
}).filter(Boolean);
res.json(decryptedPhrases);
} catch (err) {
console.error('Error fetching decrypted phrases:', err);
res.status(500).json({ error: err.message });
}
});
module.exports = router;
+2 -1
View File
@@ -135,7 +135,8 @@ router.get('/users', async (req, res) => {
router.get('/config', (req, res) => {
res.json({
defaultAgentLogin: process.env.OTRS_API_USER || '',
dailyTargetTime: parseInt(process.env.DAILY_TARGET_TIME, 10) || 480
dailyTargetTime: parseInt(process.env.DAILY_TARGET_TIME, 10) || 480,
phraseThreshold: parseInt(process.env.PHRASE_THRESHOLD, 10) || 70
});
});