// routes/charger_sessions.js const express = require('express'); const { param, query, body, validationResult } = require('express-validator'); const router = express.Router(); const verifyToken = require('../middleware/verifyToken'); const db = require('../db'); router.use(verifyToken); function handleValidation(req, res, next) { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ success: false, errors: errors.array() }); } next(); } // GET /api/charger_sessions?chargerId=... router.get( '/', [query('chargerId').isUUID().withMessage('chargerId deve ser UUID válido')], handleValidation, async (req, res) => { const { chargerId } = req.query; const userId = req.user.id; const charger = await db('chargers') .where({ id: chargerId, user_id: userId }) .first(); if (!charger) { return res .status(403) .json({ success: false, message: 'Acesso não autorizado' }); } const sessions = await db('charger_sessions') .where({ charger_id: chargerId }) .orderBy('started_at', 'desc'); res.json({ success: true, data: sessions }); } ); // GET /api/charger_sessions/:id router.get( '/:id', [param('id').isUUID().withMessage('ID de sessão inválido')], handleValidation, async (req, res) => { const { id } = req.params; const userId = req.user.id; const session = await db('charger_sessions') .join('chargers', 'charger_sessions.charger_id', 'chargers.id') .where({ 'charger_sessions.id': id, 'chargers.user_id': userId }) .select('charger_sessions.*') .first(); if (!session) { return res .status(404) .json({ success: false, message: 'Sessão não encontrada' }); } res.json({ success: true, data: session }); } ); // POST /api/charger_sessions router.post( '/', [body('charger_id').isUUID().withMessage('charger_id deve ser UUID válido')], handleValidation, async (req, res) => { const { charger_id } = req.body; const userId = req.user.id; const charger = await db('chargers') .where({ id: charger_id, user_id: userId }) .first(); if (!charger) { return res .status(403) .json({ success: false, message: 'Acesso não autorizado' }); } const [inserted] = await db('charger_sessions') .insert({ charger_id, started_at: new Date() }) .returning('*'); res.status(201).json({ success: true, data: inserted }); } ); // PUT /api/charger_sessions/:id router.put( '/:id', [ param('id').isUUID().withMessage('ID de sessão inválido'), body('ended_at').optional().isISO8601().toDate(), body('kwh').optional().isFloat({ min: 0 }), body('cost').optional().isFloat({ min: 0 }), ], handleValidation, async (req, res) => { const { id } = req.params; const { ended_at, kwh, cost } = req.body; const userId = req.user.id; const session = await db('charger_sessions') .join('chargers', 'charger_sessions.charger_id', 'chargers.id') .where({ 'charger_sessions.id': id, 'chargers.user_id': userId }) .first(); if (!session) { return res .status(404) .json({ success: false, message: 'Sessão não encontrada' }); } const [updated] = await db('charger_sessions') .where({ id }) .update({ ended_at, kwh, cost }) .returning('*'); res.json({ success: true, data: updated }); } ); // DELETE /api/charger_sessions/:id router.delete( '/:id', [param('id').isUUID().withMessage('ID de sessão inválido')], handleValidation, async (req, res) => { const { id } = req.params; const userId = req.user.id; const deleted = await db('charger_sessions') .join('chargers', 'charger_sessions.charger_id', 'chargers.id') .where({ 'charger_sessions.id': id, 'chargers.user_id': userId }) .del(); if (!deleted) { return res .status(404) .json({ success: false, message: 'Sessão não encontrada' }); } res.json({ success: true, message: 'Sessão excluída com sucesso' }); } ); // GET /api/charger_sessions/history/:chargerId router.get( '/history/:chargerId', [ param('chargerId').isUUID().withMessage('chargerId deve ser UUID válido'), query('viewMode') .isIn(['Day', 'Week', 'Month']) .withMessage('viewMode inválido'), ], handleValidation, async (req, res) => { const { chargerId } = req.params; const { viewMode } = req.query; const userId = req.user.id; // ownership check const charger = await db('chargers') .where({ id: chargerId, user_id: userId }) .first(); if (!charger) { return res .status(403) .json({ success: false, message: 'Acesso não autorizado' }); } let qb = db('charger_sessions') .where({ charger_id: chargerId }) .sum('kwh as total_kwh'); switch (viewMode) { case 'Day': qb = qb .select(db.raw('DATE(started_at) AS period')) .groupBy(db.raw('DATE(started_at)')) .orderBy('period', 'desc'); break; case 'Week': qb = qb .select( db.raw('EXTRACT(ISOYEAR FROM started_at) AS y'), db.raw('EXTRACT(WEEK FROM started_at) AS w'), db.raw( "EXTRACT(ISOYEAR FROM started_at)||'-'||LPAD(EXTRACT(WEEK FROM started_at)::text,2,'0') AS period" ) ) .groupBy('y', 'w') .orderBy([{ column: 'y', order: 'desc' }, { column: 'w', order: 'desc' }]); break; case 'Month': qb = qb .select( db.raw('EXTRACT(YEAR FROM started_at) AS y'), db.raw('EXTRACT(MONTH FROM started_at) AS m'), db.raw( "EXTRACT(YEAR FROM started_at)||'-'||LPAD(EXTRACT(MONTH FROM started_at)::text,2,'0') AS period" ) ) .groupBy('y', 'm') .orderBy([{ column: 'y', order: 'desc' }, { column: 'm', order: 'desc' }]); break; } const rows = await qb; // ✅ devolve lista vazia em vez de 404 if (!rows.length) { return res.json({ success: true, data: [] }); } const data = rows.map((r) => ({ started_at: r.period, kwh: parseFloat(r.total_kwh) || 0, })); res.json({ success: true, data }); } ); module.exports = router;