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>
83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import multer from 'multer';
|
|
import { bulkUploadMeters, generateMeterTemplate } from '../services/bulk-upload.service';
|
|
|
|
// Configure multer for memory storage
|
|
const storage = multer.memoryStorage();
|
|
|
|
export const upload = multer({
|
|
storage,
|
|
limits: {
|
|
fileSize: 10 * 1024 * 1024, // 10MB max
|
|
},
|
|
fileFilter: (_req, file, cb) => {
|
|
// Accept Excel files only
|
|
const allowedMimes = [
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
|
'application/vnd.ms-excel', // .xls
|
|
];
|
|
|
|
if (allowedMimes.includes(file.mimetype)) {
|
|
cb(null, true);
|
|
} else {
|
|
cb(new Error('Solo se permiten archivos Excel (.xlsx, .xls)'));
|
|
}
|
|
},
|
|
});
|
|
|
|
/**
|
|
* POST /api/bulk-upload/meters
|
|
* Upload Excel file with meters data
|
|
*/
|
|
export async function uploadMeters(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
if (!req.file) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: 'No se proporcionó ningún archivo',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const result = await bulkUploadMeters(req.file.buffer);
|
|
|
|
res.status(result.success ? 200 : 207).json({
|
|
success: result.success,
|
|
data: {
|
|
totalRows: result.totalRows,
|
|
inserted: result.inserted,
|
|
failed: result.errors.length,
|
|
errors: result.errors.slice(0, 50), // Limit errors in response
|
|
},
|
|
});
|
|
} catch (err) {
|
|
const error = err as Error;
|
|
console.error('Error in bulk upload:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message || 'Error procesando la carga masiva',
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/bulk-upload/meters/template
|
|
* Download Excel template for meters
|
|
*/
|
|
export async function downloadMeterTemplate(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const buffer = generateMeterTemplate();
|
|
|
|
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
res.setHeader('Content-Disposition', 'attachment; filename=plantilla_medidores.xlsx');
|
|
res.send(buffer);
|
|
} catch (err) {
|
|
const error = err as Error;
|
|
console.error('Error generating template:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: 'Error generando la plantilla',
|
|
});
|
|
}
|
|
}
|