feat: initialize microservice architecture with auth, api, realtime, copernicus, ml, and console modules

This commit is contained in:
Giuseppe Raffa
2026-03-28 15:29:34 +01:00
commit bcfce32adb
89 changed files with 12025 additions and 0 deletions

67
api/src/storage/influx.js Normal file
View File

@@ -0,0 +1,67 @@
const { InfluxDB, Point } = require('@influxdata/influxdb-client');
const url = process.env.INFLX_URL;
const token = process.env.INFLX_TOKEN;
const org = process.env.INFLX_ORG;
const boatTelemetry = "boat"
const client = new InfluxDB({ url, token })
const write = client.getWriteApi(org, boatTelemetry);
const querying = client.getQueryApi(org);
async function append(measurement, sensor, data) {
const point = new Point(measurement)
.tag("sensor", sensor)
.floatField('temperature', data.temperature)
.floatField('humidity', data.humidity)
write.writePoint(point);
await write.flush();
}
async function writeBatch(datas) {
datas.forEach(data => {
append(data.measurement, data.sensor, data.data);
})
}
async function query(bucket, relativeTime, measurement, sensor, field) {
const fluxTimeRange = relativeTime || "-1h";
let fluxQuery = `
from(bucket: "${bucket}")
|> range(start: ${fluxTimeRange})
|> filter(fn: (r) => r._measurement == "${measurement}")`;
if (sensor) {
fluxQuery += `\n |> filter(fn: (r) => r.sensor == "${sensor}")`;
}
if (field) {
fluxQuery += `\n |> filter(fn: (r) => r._field == "${field}")`;
}
fluxQuery += `\n |> yield(name: "data")`;
try {
const data = [];
for await (const { values, tableMeta } of querying.iterateRows(fluxQuery)) {
data.push(tableMeta.toObject(values));
}
return data;
} catch (error) {
console.error("Error in query:", error);
return [];
}
}
module.exports = {
write:append,
writeBatch,
query
}

136
api/src/storage/minio.js Normal file
View File

@@ -0,0 +1,136 @@
const Minio = require('minio');
const client = new Minio.Client({
endPoint: process.env.MINIO_ENDPOINT || 'localhost',
port: parseInt(process.env.MINIO_PORT) || 9000,
useSSL: process.env.MINIO_USE_SSL === 'true',
accessKey: process.env.MINIO_ACCESS_KEY,
secretKey: process.env.MINIO_SECRET_KEY
})
// Buckets
/**
*
* @param {String} bucket - Il nome del bucket
* @returns {String} - restituisce il nome del bucket creato
*/
async function bucketExists(bucket) {
const exists = await client.bucketExists(bucket);
if(!exists) {
await client.makeBucket(bucket);
}
return bucket
}
/**
* Restituisce un array con tutti i bucket sul server
* @returns {Array} - i diversi bucket presenti sul server
*/
async function getBuckets() {
const buckets = await client.listBuckets();
return buckets;
}
/**
* Restituisce i metadata del bucket passato come parametro
* @param {String} bucket - il nome del bucket
* @returns {Object} - i metadata del bucket
*/
async function getBucket(bucket) {
const buckets = await client.listBuckets();
return buckets.filter(b => b.name === bucket);
}
/**
* Restituisce un array con tutti gli oggetti presenti nel bucket passato come parametro
* @param {String} bucket - il nome del bucket
* @returns {Promise<Array>} - i file del bucket
*/
async function getObjects(bucket) {
return new Promise((resolve, reject) => {
const objects = [];
const stream = client.listObjects(bucket, '', true);
stream.on('data', obj => objects.push(obj));
stream.on('error', err => reject(err));
stream.on('end', () => resolve(objects));
});
}
/**
* Restituisce i metadata del file con id passato come parametro presente nel bucket
* @param {String} bucket - il nome del bucket
* @param {String} objectName - il nome dell'oggetto
* @returns {Object} - i metadata del file
*/
async function getObject(bucket, objectName) {
const item = await client.statObject(bucket, objectName);
return item;
}
/**
* Elimina il file con l'id passato a parametro dal bucket
* @param {String} bucket - il nome del bucket
* @param {String} objectName - il nome dell'oggetto
*/
async function removeObject(bucket, objectName) {
await client.removeObject(bucket, objectName);
}
//Upload - Download
/**
* Carica un file nel bucket come buffer
* @param {String} bucket - il nome del bucket
* @param {String} objectName - il nome che avrà il file in Minio
* @param {Buffer} fileBuffer - il file caricato
* @param {Number} size - dimensione del file
* @param {String} contentType - mimetype (es. 'image/png')
*/
async function upload(bucket, objectName, fileBuffer, size, contentType) {
await bucketExists(bucket);
const metaData = {
'Content-Type': contentType || 'application/octet-stream',
}
const result = await client.putObject(bucket, objectName, fileBuffer, size, metaData);
return result;
}
/**
* Genera un URL temporaneo di download
* @param {String} bucket - il nome del bucket
* @param {String} objectName - il nome del file
* @param {Number} expiry - quanto dura il link in secondi (default: 24h = 86400)
*
* @returns {String} url - url di download del file
*/
async function download(bucket, objectName, expiry = 86400) {
const url = await client.presignedGetObject(bucket, objectName, expiry);
return url;
}
/**
* Recupera e ritorna lo stream dei dati dal server Minio (per leggere il contenuto via API)
* @param {String} bucket - il nome del bucket
* @param {String} objectName - il nome dell'oggetto
*/
async function getFileStream(bucket, objectName) {
const dataStream = await client.getObject(bucket, objectName);
return dataStream;
}
module.exports = {
bucketExists,
getBuckets,
getBucket,
getObjects,
getObject,
removeObject,
upload,
download,
getFileStream
}

View File

@@ -0,0 +1,79 @@
const { Pool } = require('pg');
const config = {
user: process.env.PG_USER,
password: process.env.PG_PASSWORD,
host: process.env.PG_HOST,
port: process.env.PG_PORT,
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000
}
const pools = {
users: new Pool({ ...config, database: process.env.DATA_DB }),
references: new Pool({ ...config, database: process.env.REFERENCES_DB }),
sensors: new Pool({ ...config, database: process.env.SENSOR_DB || 'users' })
}
Object.entries(pools).forEach(([name, pool]) => {
pool.on('error', (err) => {
console.error(`Error in ${name} pool`, err);
})
});
/**
*
* @param {'users' | 'references'} db - the name of the database
* @returns {Promise<import('pg').PoolClient>}
*/
async function getClient(db) {
const pool = pools[db];
if (!pool) throw new Error(`Database pool type ${db} does not exist`);
return await pool.connect();
}
/**
* Esegue una query sul database specificato
* @param {string} text - Query SQL
* @param {any[]} params - Parametri
* @param {'users' | 'references'} name - Quale DB usare
*/
async function query(text, params, name = 'users') {
const client = await getClient(name);
try {
return await client.query(text, params);
} catch (error) {
console.error(`[DB Query Error on ${name}]`, error.message);
throw error;
} finally {
client.release();
}
}
/**
* Inserisce una riga in una tabella
*/
async function append(table, data, type = 'users') {
const keys = Object.keys(data);
const values = Object.values(data);
const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');
const columns = keys.join(', ');
const sql = `INSERT INTO ${table} (${columns}) VALUES (${placeholders}) RETURNING *`;
return await query(sql, values, type);
}
/**
* Rimuove una riga
*/
async function remove(table, condition, params, type = 'users') {
const sql = `DELETE FROM ${table} WHERE ${condition}`;
return await query(sql, params, type);
}
module.exports = {
query,
append,
remove,
getClient,
pools
};