feat: update session handling and add session history endpoint
This commit is contained in:
@@ -2,7 +2,7 @@ const { Pool } = require('pg');
|
||||
|
||||
const baseConfig = {
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PSW,
|
||||
password: process.env.DB_PASSWORD,
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
max: 10,
|
||||
@@ -11,14 +11,19 @@ const baseConfig = {
|
||||
};
|
||||
|
||||
const dbs = {
|
||||
data: { name: process.env.DATA_DB },
|
||||
sensors: { name: process.env.SENSORS_DB }
|
||||
data: { name: process.env.DATA_DB || 'data' },
|
||||
sensors: { name: process.env.SENSORS_DB || 'sensors' }
|
||||
}
|
||||
|
||||
const pools = {};
|
||||
|
||||
function getPool(db) {
|
||||
const dbConfig = dbs[db];
|
||||
if (!dbConfig) throw new Error(`Database ${db} not configured`);
|
||||
return new Pool({ ...baseConfig, database: dbConfig.name });
|
||||
if (!pools[db]) {
|
||||
pools[db] = new Pool({ ...baseConfig, database: dbConfig.name });
|
||||
}
|
||||
return pools[db];
|
||||
}
|
||||
|
||||
async function checkConnection(db) {
|
||||
@@ -26,8 +31,8 @@ async function checkConnection(db) {
|
||||
await getPool(db).query('SELECT NOW()');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(`Error connecting to ${db} database`, err);
|
||||
return false;
|
||||
console.error(`Error connecting to ${db} database`, err.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +43,7 @@ async function query(db, text, params) {
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
// Tabella sensori
|
||||
await query('sensors', `
|
||||
CREATE TABLE IF NOT EXISTS sensors (
|
||||
id SERIAL PRIMARY KEY,
|
||||
@@ -46,11 +52,28 @@ async function init() {
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
|
||||
// Tabella sessioni: mappa session_id (tag InfluxDB) a metadati custom
|
||||
await query('sensors', `
|
||||
CREATE TABLE IF NOT EXISTS sessiondataref (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id VARCHAR(32) UNIQUE NOT NULL,
|
||||
sensor_name VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255),
|
||||
description TEXT,
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
disconnected_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
|
||||
console.log('[DB] Tabelle verificate (sensors, sessiondataref)');
|
||||
} catch (err) {
|
||||
console.error('Error creating sensors table', err);
|
||||
console.error('[DB] Error creating tables:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
module.exports = { checkConnection, query };
|
||||
module.exports = { checkConnection, query };
|
||||
|
||||
@@ -15,11 +15,10 @@ const writeApi = client.getWriteApi(org, bucket, 'ms', {
|
||||
|
||||
/**
|
||||
* Scrive dati generici su InfluxDB senza mapping.
|
||||
* I campi vengono scritti con il nome originale.
|
||||
* @param {string} measurement - nome della measurement (es. 'logs', 'weather')
|
||||
* @param {Object} fields - campi { key: value }
|
||||
* @param {string} sensor - nome del sensore
|
||||
* @param {string} session - id sessione
|
||||
* @param {string} session - id sessione (tag immutabile)
|
||||
* @param {number} timestamp - timestamp unix ms
|
||||
*/
|
||||
function writeGenericData(measurement, fields, sensor, session, timestamp) {
|
||||
@@ -52,6 +51,24 @@ function writeForecastBatch(points, sensor, session) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forza il flush del buffer di scrittura.
|
||||
*/
|
||||
async function flush() {
|
||||
try {
|
||||
await writeApi.flush();
|
||||
} catch (err) {
|
||||
console.error('[INFLUX] Flush error:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query storica per una sessione: ritorna righe pivotate con tutti i campi.
|
||||
* @param {string} sensor - nome sensore
|
||||
* @param {string} session - session_id (tag InfluxDB)
|
||||
* @param {string} since - ISO timestamp o duration (es. "-30d")
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
async function queryHistory(sensor, session, since) {
|
||||
const queryApi = client.getQueryApi(org);
|
||||
const fluxQuery = `
|
||||
@@ -61,6 +78,7 @@ async function queryHistory(sensor, session, since) {
|
||||
|> filter(fn: (r) => r.sensor == "${sensor}")
|
||||
|> filter(fn: (r) => r.session == "${session}")
|
||||
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
|
||||
|> sort(columns: ["_time"])
|
||||
`;
|
||||
|
||||
const rows = [];
|
||||
@@ -75,4 +93,44 @@ async function queryHistory(sensor, session, since) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { writeGenericData, writeForecastBatch, queryHistory };
|
||||
/**
|
||||
* Esporta tutti i dati di una sessione come CSV.
|
||||
* @param {string} sensor - nome sensore
|
||||
* @param {string} session - session_id
|
||||
* @param {string} since - ISO timestamp inizio (opzionale, default -30d)
|
||||
* @returns {string} CSV content
|
||||
*/
|
||||
async function exportSessionCSV(sensor, session, since) {
|
||||
const start = since || '-30d';
|
||||
const rows = await queryHistory(sensor, session, start);
|
||||
|
||||
if (rows.length === 0) return '';
|
||||
|
||||
// Raccogli tutti i field names (esclusi meta InfluxDB)
|
||||
const metaKeys = new Set(['result', 'table', '_start', '_stop', '_measurement', 'sensor', 'session', '']);
|
||||
const fieldNames = new Set();
|
||||
for (const row of rows) {
|
||||
for (const key of Object.keys(row)) {
|
||||
if (!metaKeys.has(key) && key !== '_time') {
|
||||
fieldNames.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fields = Array.from(fieldNames).sort();
|
||||
const header = ['timestamp', ...fields].join(',');
|
||||
|
||||
const csvRows = rows.map(row => {
|
||||
const ts = row._time || '';
|
||||
const values = fields.map(f => {
|
||||
const v = row[f];
|
||||
if (v === null || v === undefined) return '';
|
||||
return v;
|
||||
});
|
||||
return [ts, ...values].join(',');
|
||||
});
|
||||
|
||||
return header + '\n' + csvRows.join('\n') + '\n';
|
||||
}
|
||||
|
||||
module.exports = { writeGenericData, writeForecastBatch, flush, queryHistory, exportSessionCSV };
|
||||
|
||||
Reference in New Issue
Block a user