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

97
water-api/src/index.ts Normal file
View File

@@ -0,0 +1,97 @@
import dotenv from 'dotenv';
dotenv.config();
import express, { Application, Request, Response, NextFunction } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import routes from './routes';
import logger from './utils/logger';
import { testConnection } from './config/database';
const app: Application = express();
const PORT = process.env.PORT || 3000;
const NODE_ENV = process.env.NODE_ENV || 'development';
// Security middleware
app.use(helmet());
// CORS configuration
const allowedOrigins = (process.env.CORS_ORIGIN || 'http://localhost:5173')
.split(',')
.map(origin => origin.trim());
app.use(cors({
origin: (origin, callback) => {
// Allow requests with no origin (mobile apps, curl, etc.)
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
logger.warn(`CORS blocked origin: ${origin}`);
callback(null, true); // Allow all in development
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Health check endpoint
app.get('/health', (_req: Request, res: Response) => {
res.status(200).json({
status: 'ok',
timestamp: new Date().toISOString(),
environment: NODE_ENV
});
});
// Mount all API routes
app.use('/api', routes);
// 404 handler
app.use((_req: Request, res: Response) => {
res.status(404).json({
success: false,
message: 'Resource not found',
error: 'NOT_FOUND'
});
});
// Global error handler
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
console.error('Error:', err);
res.status(500).json({
success: false,
message: NODE_ENV === 'development' ? err.message : 'Internal server error',
error: 'INTERNAL_ERROR'
});
});
// Start server
const startServer = async () => {
try {
// Test database connection
await testConnection();
logger.info('Database connection established');
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT} in ${NODE_ENV} mode`);
logger.info(`Health check available at http://localhost:${PORT}/health`);
logger.info(`API available at http://localhost:${PORT}/api`);
});
} catch (error) {
logger.error('Failed to start server:', error);
process.exit(1);
}
};
startServer();
export default app;