Migrar backend a PostgreSQL + Node.js/Express con nuevas funcionalidades

Backend (water-api/):
- Crear API REST completa con Express + TypeScript
- Implementar autenticación JWT con refresh tokens
- CRUD completo para: projects, concentrators, meters, gateways, devices, users, roles
- Agregar validación con Zod para todas las entidades
- Implementar webhooks para The Things Stack (LoRaWAN)
- Agregar endpoint de lecturas con filtros y resumen de consumo
- Implementar carga masiva de medidores via Excel (.xlsx)

Frontend:
- Crear cliente HTTP con manejo automático de JWT y refresh
- Actualizar todas las APIs para usar nuevo backend
- Agregar sistema de autenticación real (login, logout, me)
- Agregar selector de tipo (LORA, LoRaWAN, Grandes) en concentradores y medidores
- Agregar campo Meter ID en medidores
- Crear modal de carga masiva para medidores
- Agregar página de consumo con gráficas y filtros
- Corregir carga de proyectos independiente de datos existentes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Exteban08
2026-01-23 10:13:26 +00:00
parent 2b5735d78d
commit c81a18987f
92 changed files with 14088 additions and 1866 deletions

View File

@@ -0,0 +1,43 @@
import bcrypt from 'bcrypt';
import logger from './logger';
const SALT_ROUNDS = 12;
/**
* Hash a password using bcrypt
* @param password - Plain text password to hash
* @returns Hashed password
*/
export const hashPassword = async (password: string): Promise<string> => {
try {
const salt = await bcrypt.genSalt(SALT_ROUNDS);
const hashedPassword = await bcrypt.hash(password, salt);
return hashedPassword;
} catch (error) {
logger.error('Error hashing password', {
error: error instanceof Error ? error.message : 'Unknown error',
});
throw new Error('Failed to hash password');
}
};
/**
* Compare a plain text password with a hashed password
* @param password - Plain text password to compare
* @param hash - Hashed password to compare against
* @returns True if passwords match, false otherwise
*/
export const comparePassword = async (
password: string,
hash: string
): Promise<boolean> => {
try {
const isMatch = await bcrypt.compare(password, hash);
return isMatch;
} catch (error) {
logger.error('Error comparing passwords', {
error: error instanceof Error ? error.message : 'Unknown error',
});
throw new Error('Failed to compare passwords');
}
};