Add user stats endpoint with JWT authentication and data size calculation
Despliegue Automático / desplegar (push) Successful in 38s
Despliegue Automático / desplegar (push) Successful in 38s
This commit is contained in:
@@ -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 };
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user