const dotenv = require("dotenv"); const path = require("path"); const fs = require("fs"); dotenv.config({ path: path.resolve(__dirname, "..", ".env"), quiet: true }); const SIGNALK_FILES = process.env.SIGNALK_FILES || path.resolve(__dirname); function checkFolder(dirPath) { try { fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK); } catch (err) { if (err.code === 'ENOENT') { fs.mkdirSync(dirPath, { recursive: true, mode: 0o777 }); } else { throw new Error(`Permission denied for ${dirPath}`); } } return dirPath; } const paths = { base: SIGNALK_FILES, logs: checkFolder(path.join(SIGNALK_FILES, "logs")), hourlyArchive: path.join(SIGNALK_FILES, "logs", "hourly_archive.json"), logsReferences: path.join(SIGNALK_FILES, "logs", "logs_references.json"), savedDatas: checkFolder(path.join(SIGNALK_FILES, "logs", "saved_datas")), private: checkFolder(path.join(SIGNALK_FILES, "private")), authorizedAdmins: path.join(SIGNALK_FILES, "private", "authorized_admins.txt"), telegramUsers: path.join(SIGNALK_FILES, "private", "telegram_users.json"), sensorsReferences: path.join(__dirname, "sensors", "sensors.references.json") }; const config = { telegramBotToken: process.env.TELEGRAM_BOT_TOKEN, mapboxKey: process.env.MAPBOX_KEY, cloudUrl: process.env.CLOUD_URL || "https://realtime.mebcloud.it", cloudApiKey: process.env.CLOUD_API_KEY, realtimeUrl: process.env.REALTIME_URL || 'http://realtime:3002', paths }; /** * Carica la configurazione sensori dal server (con fallback locale). * Se viene fornito un ticket, usa l'endpoint autenticato /sensors/data/references. * Altrimenti usa l'endpoint pubblico /sensors/references (legacy). * * @param {string|null} ticket - Ticket di autenticazione (opzionale) * @returns {Promise} { items, version, isActive } o null */ async function loadSensorReferencesFromServer(ticket = null) { // Tentativo 1: Endpoint autenticato (con ticket) if (ticket) { const authUrl = config.realtimeUrl + '/sensors/data/references'; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const res = await fetch(authUrl, { signal: controller.signal, headers: { 'Authorization': `Bearer ${ticket}` } }); clearTimeout(timeout); if (res.ok) { const data = await res.json(); console.log(`[MEB] Sensor references caricati (autenticato, v: ${data.version})`); return data; } console.warn(`[MEB] Endpoint autenticato HTTP ${res.status}, provo fallback pubblico`); } catch (err) { console.warn(`[MEB] Endpoint autenticato fallito: ${err.message}, provo fallback pubblico`); } } // Tentativo 2: Endpoint pubblico (legacy) const publicUrl = config.realtimeUrl + '/sensors/references'; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const res = await fetch(publicUrl, { signal: controller.signal }); clearTimeout(timeout); if (res.ok) { const data = await res.json(); console.log(`[MEB] Sensor references caricati dal server (versione: ${data.version})`); return data; } console.warn(`[MEB] Server sensor refs HTTP ${res.status}, uso fallback locale`); } catch (err) { console.warn(`[MEB] Impossibile caricare sensor refs dal server: ${err.message}`); } // Tentativo 3: File locale try { const raw = fs.readFileSync(paths.sensorsReferences, 'utf-8'); const data = JSON.parse(raw); console.log(`[MEB] Sensor references caricati da file locale (versione: ${data.version})`); return data; } catch (err) { console.error(`[MEB] Nessuna sorgente sensor refs disponibile: ${err.message}`); return null; } } /** * Controlla se la versione dei sensor references sul server e' cambiata. * Ritorna la nuova versione se diversa, null altrimenti. */ async function checkSensorReferencesVersion(currentVersion) { const url = config.realtimeUrl + '/sensors/references/version'; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 3000); const res = await fetch(url, { signal: controller.signal }); clearTimeout(timeout); if (res.ok) { const data = await res.json(); if (data.version && data.version !== currentVersion) { return data.version; } } } catch { // Silenzioso: il polling della versione non e' critico } return null; } module.exports = { config, paths, loadSensorReferencesFromServer, checkSensorReferencesVersion };