feat: update session handling and add session history endpoint
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
const router = require('express').Router();
|
||||
const { queryAll, query, hset } = require('../store/redis');
|
||||
const { connectedSensors } = require('../ws/handler');
|
||||
const { flush, exportSessionCSV } = require('../store/influx');
|
||||
const db = require('../store/db');
|
||||
|
||||
/**
|
||||
* GET /sessions — Lista tutte le sessioni dei sensori con metadata e rules versions
|
||||
* GET /sessions — Lista tutte le sessioni attive dei sensori
|
||||
*/
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
@@ -14,15 +16,9 @@ router.get('/', async (req, res) => {
|
||||
const info = await query(name, 'sensors');
|
||||
sessions[name] = {
|
||||
name,
|
||||
connectedAt: info.timestamp || null,
|
||||
connectedAt: info.connectedAt || info.timestamp || null,
|
||||
session: info.session || null,
|
||||
sessionLabel: info.sessionLabel || info.session || null,
|
||||
status: info.status || 'unknown',
|
||||
rules: {
|
||||
weather: info.rules_weather || null,
|
||||
data: info.rules_data || null,
|
||||
logs: info.rules_logs || null,
|
||||
}
|
||||
status: info.status || 'unknown'
|
||||
};
|
||||
}
|
||||
res.json(sessions);
|
||||
@@ -32,6 +28,21 @@ router.get('/', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/history — Lista tutte le sessioni passate (da sessiondataref)
|
||||
*/
|
||||
router.get('/history', async (req, res) => {
|
||||
try {
|
||||
const result = await db.query('sensors',
|
||||
`SELECT * FROM sessiondataref ORDER BY created_at DESC LIMIT 100`
|
||||
);
|
||||
res.json(result.rows);
|
||||
} catch (err) {
|
||||
console.error('Error fetching session history:', err.message);
|
||||
res.status(500).json({ error: 'internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/pending — Lista token di connessione pendenti
|
||||
*/
|
||||
@@ -67,7 +78,7 @@ router.get('/connected', async (req, res) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/connected/:id — Verifica se un sensore specifico è connesso
|
||||
* GET /sessions/connected/:id — Verifica se un sensore specifico e connesso
|
||||
*/
|
||||
router.get('/connected/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
@@ -84,37 +95,161 @@ router.get('/connected/:id', async (req, res) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /sessions/:id/label — Cambia il label della sessione per un sensore connesso.
|
||||
* Non interrompe il flusso dati. I nuovi punti InfluxDB avranno il nuovo tag.
|
||||
* POST /sessions/:id/flush — Forza il flush del buffer InfluxDB
|
||||
*/
|
||||
router.post('/:id/label', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { label } = req.body;
|
||||
|
||||
if (!label || typeof label !== 'string' || label.trim().length === 0) {
|
||||
return res.status(400).json({ error: 'label is required' });
|
||||
}
|
||||
|
||||
const trimmedLabel = label.trim();
|
||||
|
||||
// Trova il WS client connesso
|
||||
const ws = connectedSensors.get(id);
|
||||
if (!ws) {
|
||||
return res.status(404).json({ error: 'sensor not connected' });
|
||||
}
|
||||
|
||||
// Aggiorna in memoria (effetto immediato sui prossimi punti InfluxDB)
|
||||
ws.sessionLabel = trimmedLabel;
|
||||
|
||||
// Aggiorna in Redis per persistenza
|
||||
router.post('/:id/flush', async (req, res) => {
|
||||
try {
|
||||
await hset(`sensors:${id}`, 'sessionLabel', trimmedLabel);
|
||||
await flush();
|
||||
res.json({ status: 'ok' });
|
||||
} catch (err) {
|
||||
console.error('Error updating session label in Redis', err);
|
||||
console.error('Error flushing:', err.message);
|
||||
res.status(500).json({ error: 'flush failed' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/:id/csv — Esporta tutti i dati della sessione come CSV.
|
||||
* Usa il session_id da Redis (sensore connesso) oppure il query param ?session=sXXXX.
|
||||
* Il CSV contiene tutti i dati logs per quel sensor + session da InfluxDB.
|
||||
*/
|
||||
router.get('/:id/csv', async (req, res) => {
|
||||
const sensorName = req.params.id;
|
||||
|
||||
try {
|
||||
// Determina il session_id: da query param, da Redis, o dall'ultimo in DB
|
||||
let sessionId = req.query.session || null;
|
||||
|
||||
if (!sessionId) {
|
||||
// Prova da Redis (sensore connesso)
|
||||
const info = await query(sensorName, 'sensors');
|
||||
sessionId = info?.session || null;
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
// Ultima sessione in sessiondataref
|
||||
const result = await db.query('sensors',
|
||||
`SELECT session_id FROM sessiondataref WHERE sensor_name = $1 ORDER BY created_at DESC LIMIT 1`,
|
||||
[sensorName]
|
||||
);
|
||||
sessionId = result.rows[0]?.session_id || null;
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
return res.status(404).json({ error: 'No session found for this sensor' });
|
||||
}
|
||||
|
||||
// Determina il range temporale: da connectedAt della sessione
|
||||
let since = req.query.from ? new Date(parseInt(req.query.from)).toISOString() : null;
|
||||
|
||||
if (!since) {
|
||||
// Cerca il created_at nella sessiondataref
|
||||
const result = await db.query('sensors',
|
||||
`SELECT created_at FROM sessiondataref WHERE session_id = $1`,
|
||||
[sessionId]
|
||||
);
|
||||
since = result.rows[0]?.created_at?.toISOString() || '-30d';
|
||||
}
|
||||
|
||||
const csv = await exportSessionCSV(sensorName, sessionId, since);
|
||||
|
||||
if (!csv) {
|
||||
return res.status(404).json({ error: 'No data found for this session' });
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="session_${sessionId}_${sensorName}.csv"`);
|
||||
res.send(csv);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error exporting CSV:', err.message);
|
||||
res.status(500).json({ error: 'CSV export failed' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /sessions/:id/details — Ottieni i dettagli della sessione corrente
|
||||
*/
|
||||
router.get('/:id/details', async (req, res) => {
|
||||
const sensorName = req.params.id;
|
||||
const sessionId = req.query.session || null;
|
||||
|
||||
try {
|
||||
let result;
|
||||
if (sessionId) {
|
||||
result = await db.query('sensors',
|
||||
`SELECT * FROM sessiondataref WHERE session_id = $1`,
|
||||
[sessionId]
|
||||
);
|
||||
} else {
|
||||
// Ultima sessione per questo sensore
|
||||
result = await db.query('sensors',
|
||||
`SELECT * FROM sessiondataref WHERE sensor_name = $1 ORDER BY created_at DESC LIMIT 1`,
|
||||
[sensorName]
|
||||
);
|
||||
}
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Session not found' });
|
||||
}
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (err) {
|
||||
console.error('Error fetching session details:', err.message);
|
||||
res.status(500).json({ error: 'internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /sessions/:id/details — Aggiorna nome, descrizione o tags della sessione.
|
||||
* Body: { name?, description?, tags? }
|
||||
*/
|
||||
router.put('/:id/details', async (req, res) => {
|
||||
const sensorName = req.params.id;
|
||||
const { session: sessionId, name, description, tags } = req.body;
|
||||
|
||||
if (!sessionId) {
|
||||
return res.status(400).json({ error: 'session id is required in body' });
|
||||
}
|
||||
|
||||
console.log(`[${id}] Session label changed to: ${trimmedLabel}`);
|
||||
res.json({ status: 'ok', label: trimmedLabel });
|
||||
try {
|
||||
const updates = [];
|
||||
const values = [];
|
||||
let idx = 1;
|
||||
|
||||
if (name !== undefined) {
|
||||
updates.push(`name = $${idx++}`);
|
||||
values.push(name);
|
||||
}
|
||||
if (description !== undefined) {
|
||||
updates.push(`description = $${idx++}`);
|
||||
values.push(description);
|
||||
}
|
||||
if (tags !== undefined) {
|
||||
updates.push(`tags = $${idx++}`);
|
||||
values.push(tags);
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
return res.status(400).json({ error: 'No fields to update' });
|
||||
}
|
||||
|
||||
updates.push(`updated_at = NOW()`);
|
||||
values.push(sessionId);
|
||||
|
||||
const result = await db.query('sensors',
|
||||
`UPDATE sessiondataref SET ${updates.join(', ')} WHERE session_id = $${idx} RETURNING *`,
|
||||
values
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Session not found' });
|
||||
}
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (err) {
|
||||
console.error('Error updating session details:', err.message);
|
||||
res.status(500).json({ error: 'internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user