Initial commit
This commit is contained in:
264
projeto_parte2.c
Normal file
264
projeto_parte2.c
Normal file
@@ -0,0 +1,264 @@
|
||||
|
||||
|
||||
// === Início de: ./package.json ===
|
||||
{
|
||||
"name": "evse-backend",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"axios": "^1.13.2",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"express-rate-limit": "^8.2.1",
|
||||
"express-validator": "^7.2.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"knex": "^3.1.0",
|
||||
"mqtt": "^5.13.1",
|
||||
"pg": "^8.16.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"web-push": "^3.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
|
||||
// === Fim de: ./package.json ===
|
||||
|
||||
|
||||
// === Início de: ./server.js ===
|
||||
// server.js
|
||||
const http = require('http');
|
||||
const { Server } = require('socket.io');
|
||||
const app = require('./app');
|
||||
const db = require('./db');
|
||||
const jwt = require('jsonwebtoken');
|
||||
require('dotenv').config();
|
||||
|
||||
if (!process.env.JWT_SECRET) {
|
||||
throw new Error('JWT_SECRET não definido no .env');
|
||||
}
|
||||
|
||||
if (!process.env.MQTT_URL) {
|
||||
console.warn('Warning: MQTT_URL is not defined.');
|
||||
}
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
const origins = process.env.CORS_ORIGIN
|
||||
? process.env.CORS_ORIGIN.split(',').map((s) => s.trim())
|
||||
: ['http://localhost:5173'];
|
||||
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: origins,
|
||||
methods: ['GET', 'POST'],
|
||||
credentials: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { on } = require('./mqtt/client');
|
||||
console.log('MQTT client initialized.');
|
||||
|
||||
// ---------------------------
|
||||
// Helpers de normalização
|
||||
// ---------------------------
|
||||
const toNum = (v) => {
|
||||
if (v === null || v === undefined || v === '') return 0;
|
||||
const n = typeof v === 'number' ? v : parseFloat(v);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
};
|
||||
|
||||
const toArr3 = (v) => {
|
||||
if (Array.isArray(v)) {
|
||||
return [toNum(v[0]), toNum(v[1]), toNum(v[2])];
|
||||
}
|
||||
// se vier como objeto {l1,l2,l3}
|
||||
if (v && typeof v === 'object') {
|
||||
return [toNum(v.l1), toNum(v.l2), toNum(v.l3)];
|
||||
}
|
||||
return [0, 0, 0];
|
||||
};
|
||||
|
||||
const normalizeStatus = (rawStatus) => {
|
||||
const s = String(rawStatus || '').toLowerCase();
|
||||
|
||||
if (s.includes('charging')) return '⚡ Charging';
|
||||
if (s.includes('ready')) return '🟢 Ready';
|
||||
if (s.includes('fault') || s.includes('error')) return '⚠️ Fault';
|
||||
if (s.includes('wait')) return '⚡ Wait';
|
||||
if (s.includes('not conn') || s.includes('disconnected')) return '🔌 Not Conn.';
|
||||
if (s.includes('vent')) return '💨 Vent';
|
||||
|
||||
// fallback: devolve string original se não bater em nada
|
||||
return rawStatus || '—';
|
||||
};
|
||||
|
||||
// Normaliza eventos de status (realtime)
|
||||
function normalizeChargingStatus(data = {}) {
|
||||
const chargerId = data.charger_id || data.chargerId || data.id;
|
||||
|
||||
const powerArr = toArr3(data.power || data.raw?.power);
|
||||
const voltageArr = toArr3(data.voltage || data.raw?.voltage);
|
||||
const currentArr = toArr3(data.current || data.raw?.current);
|
||||
|
||||
const status =
|
||||
normalizeStatus(data.status || data.state || data.raw?.state);
|
||||
|
||||
const chargingTime =
|
||||
toNum(data.charging_time) ||
|
||||
toNum(data.chargingTime) ||
|
||||
toNum(data.raw?.chargingTime) ||
|
||||
toNum(data.raw?.sessionTime);
|
||||
|
||||
const consumption =
|
||||
toNum(data.consumption) ||
|
||||
toNum(data.raw?.consumption);
|
||||
|
||||
const chargingCurrent =
|
||||
toNum(data.charging_current) ||
|
||||
toNum(data.chargingCurrent) ||
|
||||
currentArr[0];
|
||||
|
||||
return {
|
||||
charger_id: chargerId,
|
||||
mqtt_topic: data.mqtt_topic || data.mqttTopic,
|
||||
|
||||
status,
|
||||
stateCode: data.stateCode || data.raw?.stateCode || undefined,
|
||||
|
||||
consumption,
|
||||
charging_time: chargingTime,
|
||||
charging_current: chargingCurrent,
|
||||
|
||||
power: powerArr,
|
||||
voltage: voltageArr,
|
||||
current: currentArr,
|
||||
|
||||
raw: data.raw || data, // mantém raw p/ debug, mas já limpinho
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// Normaliza eventos de config (quando o carregador manda config)
|
||||
function normalizeChargingConfig(data = {}) {
|
||||
const chargerId = data.charger_id || data.chargerId || data.id;
|
||||
|
||||
// se vierem chaves diferentes, mapeia
|
||||
const cfg = data.config || data.raw?.config || data;
|
||||
|
||||
return {
|
||||
charger_id: chargerId,
|
||||
mqtt_topic: data.mqtt_topic || data.mqttTopic,
|
||||
config: {
|
||||
max_charging_current:
|
||||
cfg.max_charging_current ??
|
||||
cfg.maxChargingCurrent ??
|
||||
cfg.max_current ??
|
||||
cfg.maxCurrent ??
|
||||
undefined,
|
||||
|
||||
require_auth:
|
||||
cfg.require_auth ??
|
||||
cfg.requireAuth ??
|
||||
undefined,
|
||||
|
||||
rcm_enabled:
|
||||
cfg.rcm_enabled ??
|
||||
cfg.rcmEnabled ??
|
||||
undefined,
|
||||
|
||||
temperature_limit:
|
||||
cfg.temperature_limit ??
|
||||
cfg.temperatureThreshold ??
|
||||
cfg.temp_limit ??
|
||||
undefined,
|
||||
},
|
||||
raw: data.raw || data,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// auth middleware do socket
|
||||
// ---------------------------
|
||||
io.use((socket, next) => {
|
||||
const token = socket.handshake.auth.token;
|
||||
if (!token) return next(new Error('Authentication error: token required'));
|
||||
|
||||
try {
|
||||
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
||||
socket.user = payload;
|
||||
next();
|
||||
} catch (err) {
|
||||
next(new Error('Authentication error'));
|
||||
}
|
||||
});
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`Client connected: ${socket.id}, user: ${socket.user.username}`);
|
||||
|
||||
// join rooms apenas do user autenticado
|
||||
socket.on('joinChargers', async (chargerIds = []) => {
|
||||
try {
|
||||
if (!Array.isArray(chargerIds) || chargerIds.length === 0) return;
|
||||
|
||||
const rows = await db('chargers')
|
||||
.whereIn('id', chargerIds)
|
||||
.andWhere({ user_id: socket.user.id })
|
||||
.select('id');
|
||||
|
||||
const allowed = rows.map((r) => r.id);
|
||||
allowed.forEach((id) => socket.join(id));
|
||||
|
||||
console.log(`Socket ${socket.id} joined chargers: ${allowed}`);
|
||||
} catch (err) {
|
||||
console.error('joinChargers error:', err);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('charger-action', ({ chargerId, action, ampLimit }) => {
|
||||
console.log(
|
||||
`Received action "${action}" for charger ${chargerId} by user ${socket.user.id}`
|
||||
);
|
||||
io.to(chargerId).emit('charger-action-status', 'success');
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log(`Client disconnected: ${socket.id}, reason: ${reason}`);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------
|
||||
// Relay MQTT -> Socket.IO (NORMALIZADO)
|
||||
// ---------------------------
|
||||
on('charging-status', (data) => {
|
||||
const normalized = normalizeChargingStatus(data);
|
||||
const chargerId = normalized.charger_id;
|
||||
if (!chargerId) return;
|
||||
|
||||
io.to(chargerId).emit('charging-status', normalized);
|
||||
});
|
||||
|
||||
on('charging-config', (data) => {
|
||||
const normalized = normalizeChargingConfig(data);
|
||||
const chargerId = normalized.charger_id;
|
||||
if (!chargerId) return;
|
||||
|
||||
io.to(chargerId).emit('charging-config', normalized);
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server listening on http://localhost:${PORT}`);
|
||||
});
|
||||
|
||||
// === Fim de: ./server.js ===
|
||||
Reference in New Issue
Block a user