feat: add support for later forecasts and implement force update functionality for rules

This commit is contained in:
Giuseppe Raffa
2026-04-16 08:14:10 +02:00
parent c0be21a718
commit edd7226966
8 changed files with 481 additions and 20 deletions

View File

@@ -2,23 +2,18 @@ const router = require('express').Router();
const crypto = require('crypto');
const { query } = require('../storage/postgres');
const sets = ['forecasts', 'sensors'];
const sets = ['forecasts', 'sensors', 'marine'];
function hashSensorCode(code) {
return crypto.createHash('sha256').update(code).digest('hex');
}
/**
* GET /params/sensor/:sensorCode/active?set=sensors
* Autenticazione tramite SENSOR_CODE (stesso meccanismo di realtime)
* Middleware: valida sensor code e verifica che il sensore sia attivo.
* Salva sensor.id in req.sensorId.
*/
router.get('/:sensorCode/active', async (req, res) => {
async function authenticateSensor(req, res, next) {
const { sensorCode } = req.params;
const { set } = req.query;
if (!set || !sets.includes(set))
return res.status(400).json({ error: 'SET parameter invalid' });
try {
const hashed = hashSensorCode(sensorCode);
const sensor = await query(
@@ -30,11 +25,29 @@ router.get('/:sensorCode/active', async (req, res) => {
if (!sensor.rows[0]) {
return res.status(401).json({ error: 'Sensor code not valid' });
}
if (!sensor.rows[0].active) {
return res.status(403).json({ error: 'Sensor is not active' });
}
req.sensorId = sensor.rows[0].id;
next();
} catch (err) {
console.error('[PARAMS/SENSOR] Auth error:', err.message);
res.status(500).json({ error: 'Internal server error' });
}
}
/**
* GET /params/sensor/:sensorCode/active?set=sensors
* Ritorna il set di parametri attivo (forecasts, sensors, marine)
*/
router.get('/:sensorCode/active', authenticateSensor, async (req, res) => {
const { set } = req.query;
if (!set || !sets.includes(set))
return res.status(400).json({ error: 'SET parameter invalid' });
try {
const result = await query(
`SELECT * FROM ${set} WHERE active = true LIMIT 1`,
[],
@@ -48,4 +61,55 @@ router.get('/:sensorCode/active', async (req, res) => {
}
});
// --- Mapping tipo rules → tabelle ---
const RULES_TYPE_MAP = {
weather: { rules: 'weather', items: 'weatheritems' },
laterforecasts: { rules: 'laterforecasts', items: 'laterforecastitems' },
data: { rules: 'dataread', items: 'datareaditems' },
logs: { rules: 'logs', items: 'logitems' }
};
/**
* GET /params/sensor/:sensorCode/rules?type=weather
* Ritorna la rule attiva con items per il tipo specificato.
* Autenticazione tramite sensor code (non richiede JWT/API key).
*/
router.get('/:sensorCode/rules', authenticateSensor, async (req, res) => {
const { type } = req.query;
if (!type || !RULES_TYPE_MAP[type]) {
return res.status(400).json({ error: `invalid type, must be one of: ${Object.keys(RULES_TYPE_MAP).join(', ')}` });
}
const tables = RULES_TYPE_MAP[type];
try {
const { rows: ruleRows } = await query(
`SELECT * FROM ${tables.rules} WHERE active = true AND archived = false LIMIT 1`,
[], 'rules'
);
if (ruleRows.length === 0) {
return res.status(404).json({ error: `no active ${type} rule found` });
}
const rule = ruleRows[0];
const { rows: items } = await query(
`SELECT * FROM ${tables.items} WHERE rule_id = $1 AND enabled = true`,
[rule.id], 'rules'
);
res.json({
id: rule.id,
version: rule.version,
description: rule.description,
tags: rule.tags,
items
});
} catch (err) {
console.error('[PARAMS/SENSOR] Rules error:', err.message);
res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = router;