Add user stats endpoint with JWT authentication and data size calculation
Despliegue Automático / desplegar (push) Successful in 38s

This commit is contained in:
2026-08-18 16:16:22 +02:00
parent a139f1f572
commit 1618cbfd40
2 changed files with 82 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
const jwt = require('jsonwebtoken');
const Note = require('../models/Note');
const Category = require('../models/Category');
const JWT_SECRET = process.env.JWT_SECRET;
const getStats = async (req, res) => {
try {
// 1. Verificamos el token (igual que en syncController)
const authorizationHeader = req.headers.authorization || '';
if (!authorizationHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Authorization header missing' });
}
const token = authorizationHeader.slice(7).trim();
const payload = jwt.verify(token, JWT_SECRET);
const userId = payload && payload.id;
if (!userId) return res.status(401).json({ error: 'Usuario inválido' });
// 2. Traemos solo los campos pesados para no saturar la RAM del servidor
const notes = await Note.findAll({
where: { userId },
attributes: ['title', 'body', 'permanentDelete']
});
const categories = await Category.findAll({
where: { userId },
attributes: ['encrypted_name']
});
// 3. Calculamos el tamaño exacto en Bytes usando Buffer
let totalBytes = 0;
notes.forEach(note => {
// Si está borrada permanentemente no ocupa texto real
if (note.permanentDelete) return;
if (note.title) totalBytes += Buffer.byteLength(note.title, 'utf8');
if (note.body) totalBytes += Buffer.byteLength(note.body, 'utf8');
// Sumamos ~150 bytes por nota para representar el peso de los UUIDs (id), fechas y booleanos
totalBytes += 150;
});
categories.forEach(cat => {
if (cat.encrypted_name) totalBytes += Buffer.byteLength(cat.encrypted_name, 'utf8');
totalBytes += 100; // Margen para UUIDs, colores e iconos
});
// 4. Formateamos el resultado
let formattedSize = '0 KB';
if (totalBytes > 0) {
if (totalBytes < 1024 * 1024) {
formattedSize = `${(totalBytes / 1024).toFixed(2)} KB`;
} else {
formattedSize = `${(totalBytes / (1024 * 1024)).toFixed(2)} MB`;
}
}
res.json({
totalNotes: notes.length,
totalCategories: categories.length,
sizeBytes: totalBytes,
formattedSize: formattedSize
});
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token inválido o expirado' });
}
res.status(500).json({ error: error.message });
}
};
module.exports = { getStats };
+8
View File
@@ -0,0 +1,8 @@
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
// Ruta GET protegida por token
router.get('/user/stats', userController.getStats);
module.exports = router;