Add delete-all-data endpoint to auth API for user data removal
Despliegue Automático / desplegar (push) Successful in 1m7s
Despliegue Automático / desplegar (push) Successful in 1m7s
This commit is contained in:
+16
@@ -147,6 +147,22 @@ Respuesta ejemplo:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `POST /api/auth/delete-all-data`
|
||||||
|
|
||||||
|
Elimina todas las notas, categorías y refresh tokens del usuario autenticado. El usuario se conserva.
|
||||||
|
|
||||||
|
Headers:
|
||||||
|
|
||||||
|
- `Authorization: Bearer <accessToken>`: obligatorio.
|
||||||
|
|
||||||
|
Respuesta ejemplo:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Datos del usuario eliminados correctamente; se eliminaron todos los tokens."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `POST /api/sync`
|
### `POST /api/sync`
|
||||||
|
|
||||||
Endpoint único de sincronización offline-first.
|
Endpoint único de sincronización offline-first.
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
const authService = require('../services/authService');
|
const authService = require('../services/authService');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const JWT_SECRET = process.env.JWT_SECRET;
|
||||||
|
|
||||||
|
if (!JWT_SECRET) {
|
||||||
|
throw new Error('JWT_SECRET no está definido');
|
||||||
|
}
|
||||||
|
|
||||||
// 1. REGISTRO
|
// 1. REGISTRO
|
||||||
const register = async (req, res) => {
|
const register = async (req, res) => {
|
||||||
@@ -73,5 +80,30 @@ const logout = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ¡EXPORTAMOS LAS 4 FUNCIONES!
|
const deleteAllData = async (req, res) => {
|
||||||
module.exports = { register, login, refresh, logout };
|
try {
|
||||||
|
const authorizationHeader = req.headers.authorization || '';
|
||||||
|
if (!authorizationHeader.startsWith('Bearer ')) {
|
||||||
|
return res.status(401).json({ error: 'Authorization header missing or invalid' });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 en token' });
|
||||||
|
|
||||||
|
const result = await authService.deleteAllUserData(userId);
|
||||||
|
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
||||||
|
return res.status(401).json({ error: 'Token inválido o expirado' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusCode = error.message.includes('obligatorio') || error.message.includes('inválido') ? 400 : 500;
|
||||||
|
res.status(statusCode).json({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exportamos los handlers de auth.
|
||||||
|
module.exports = { register, login, refresh, logout, deleteAllData };
|
||||||
@@ -14,4 +14,7 @@ router.post('/refresh', authController.refresh);
|
|||||||
// POST /api/auth/logout
|
// POST /api/auth/logout
|
||||||
router.post('/logout', authController.logout);
|
router.post('/logout', authController.logout);
|
||||||
|
|
||||||
|
// POST /api/auth/delete-all-data
|
||||||
|
router.post('/delete-all-data', authController.deleteAllData);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const sequelize = require('../config/database');
|
||||||
const User = require('../models/User');
|
const User = require('../models/User');
|
||||||
|
const Note = require('../models/Note');
|
||||||
|
const Category = require('../models/Category');
|
||||||
const RefreshToken = require('../models/RefreshToken');
|
const RefreshToken = require('../models/RefreshToken');
|
||||||
|
|
||||||
const JWT_SECRET = process.env.JWT_SECRET;
|
const JWT_SECRET = process.env.JWT_SECRET;
|
||||||
@@ -85,6 +88,22 @@ class AuthService {
|
|||||||
await RefreshToken.destroy({ where: { userId } });
|
await RefreshToken.destroy({ where: { userId } });
|
||||||
return { message: 'Se ha cerrado sesión en todos los dispositivos de forma global.' };
|
return { message: 'Se ha cerrado sesión en todos los dispositivos de forma global.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteAllUserData(userId) {
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error('userId es obligatorio');
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequelize.transaction(async (transaction) => {
|
||||||
|
await Note.destroy({ where: { userId }, transaction });
|
||||||
|
await Category.destroy({ where: { userId }, transaction });
|
||||||
|
await RefreshToken.destroy({ where: { userId }, transaction });
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Datos del usuario eliminados correctamente; se eliminaron todos los tokens.'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new AuthService();
|
module.exports = new AuthService();
|
||||||
Reference in New Issue
Block a user