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