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,222 @@
import { Response } from 'express';
import { AuthenticatedRequest } from '../middleware/auth.middleware';
import * as roleService from '../services/role.service';
import { CreateRoleInput, UpdateRoleInput } from '../validators/role.validator';
/**
* GET /roles
* List all roles (all authenticated users)
*/
export async function getAllRoles(
_req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const roles = await roleService.getAll();
res.status(200).json({
success: true,
message: 'Roles retrieved successfully',
data: roles,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to retrieve roles';
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* GET /roles/:id
* Get a single role by ID with user count (all authenticated users)
*/
export async function getRoleById(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const roleId = parseInt(req.params.id, 10);
if (isNaN(roleId)) {
res.status(400).json({
success: false,
error: 'Invalid role ID',
});
return;
}
const role = await roleService.getById(roleId);
if (!role) {
res.status(404).json({
success: false,
error: 'Role not found',
});
return;
}
res.status(200).json({
success: true,
message: 'Role retrieved successfully',
data: role,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to retrieve role';
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* POST /roles
* Create a new role (admin only)
*/
export async function createRole(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const data = req.body as CreateRoleInput;
const role = await roleService.create({
name: data.name,
description: data.description,
permissions: data.permissions as Record<string, unknown> | undefined,
});
res.status(201).json({
success: true,
message: 'Role created successfully',
data: role,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to create role';
if (message === 'Role name already exists') {
res.status(409).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* PUT /roles/:id
* Update a role (admin only)
*/
export async function updateRole(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const roleId = parseInt(req.params.id, 10);
if (isNaN(roleId)) {
res.status(400).json({
success: false,
error: 'Invalid role ID',
});
return;
}
const data = req.body as UpdateRoleInput;
const role = await roleService.update(roleId, {
name: data.name,
description: data.description,
permissions: data.permissions as Record<string, unknown> | undefined,
});
if (!role) {
res.status(404).json({
success: false,
error: 'Role not found',
});
return;
}
res.status(200).json({
success: true,
message: 'Role updated successfully',
data: role,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to update role';
if (message === 'Role name already exists') {
res.status(409).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* DELETE /roles/:id
* Delete a role (admin only, only if no users assigned)
*/
export async function deleteRole(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const roleId = parseInt(req.params.id, 10);
if (isNaN(roleId)) {
res.status(400).json({
success: false,
error: 'Invalid role ID',
});
return;
}
const deleted = await roleService.deleteRole(roleId);
if (!deleted) {
res.status(404).json({
success: false,
error: 'Role not found',
});
return;
}
res.status(200).json({
success: true,
message: 'Role deleted successfully',
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete role';
// Handle case where users are assigned to the role
if (message.includes('Cannot delete role')) {
res.status(409).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}