refact
This commit is contained in:
2
.env
2
.env
@@ -1,6 +1,8 @@
|
|||||||
DATABASE_URL=postgres://postgres:123QWEasd@localhost:5432/evstation
|
DATABASE_URL=postgres://postgres:123QWEasd@localhost:5432/evstation
|
||||||
PORT=4000
|
PORT=4000
|
||||||
|
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
JWT_SECRET=umaChaveMuitoForteESegura123!
|
JWT_SECRET=umaChaveMuitoForteESegura123!
|
||||||
|
|
||||||
MQTT_URL=mqtt://localhost:1883
|
MQTT_URL=mqtt://localhost:1883
|
||||||
|
|||||||
49
app.js
49
app.js
@@ -1,49 +0,0 @@
|
|||||||
// app.js
|
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
// Enforce presence of JWT_SECRET
|
|
||||||
if (!process.env.JWT_SECRET) {
|
|
||||||
console.error('Error: JWT_SECRET is not defined in environment.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
|
|
||||||
// Route modules
|
|
||||||
const usersRoutes = require('./routes/users');
|
|
||||||
const chargerRoutes = require('./routes/chargers');
|
|
||||||
const sessionsRoutes = require('./routes/charger_sessions');
|
|
||||||
const pushRoutes = require('./routes/push'); // ✅ ADICIONA ISTO
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
// Global middlewares
|
|
||||||
app.use(express.json());
|
|
||||||
|
|
||||||
// Public routes (no auth)
|
|
||||||
app.use('/api/users', usersRoutes);
|
|
||||||
|
|
||||||
// Protected routes
|
|
||||||
// ✅ NOTA: chargers/sessions/push já têm router.use(verifyToken) internamente
|
|
||||||
// portanto não precisas passar verifyToken aqui.
|
|
||||||
app.use('/api/chargers', chargerRoutes);
|
|
||||||
app.use('/api/charger_sessions', sessionsRoutes);
|
|
||||||
app.use('/api/push', pushRoutes); // ✅ ISTO resolve /api/push/vapid-public-key
|
|
||||||
|
|
||||||
// Health check opcional (bom para produção)
|
|
||||||
app.get('/api/health', (req, res) => {
|
|
||||||
res.json({ success: true, ok: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Error handler (catch-all)
|
|
||||||
app.use((err, req, res, next) => {
|
|
||||||
console.error('Unhandled error:', err);
|
|
||||||
res
|
|
||||||
.status(err.status || 500)
|
|
||||||
.json({
|
|
||||||
success: false,
|
|
||||||
message: err.message || 'Internal Server Error',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = app;
|
|
||||||
15
db.js
15
db.js
@@ -1,15 +0,0 @@
|
|||||||
// ./db.js
|
|
||||||
require('dotenv').config();
|
|
||||||
const knex = require('knex');
|
|
||||||
const knexfile = require('./knexfile');
|
|
||||||
|
|
||||||
// Pega o ambiente (default pra 'development')
|
|
||||||
const environment = process.env.NODE_ENV || 'development';
|
|
||||||
|
|
||||||
// Recupera só as configurações daquele ambiente
|
|
||||||
const config = knexfile[environment];
|
|
||||||
|
|
||||||
// Inicializa o Knex com esse config
|
|
||||||
const db = knex(config);
|
|
||||||
|
|
||||||
module.exports = db;
|
|
||||||
52
knexfile.js
52
knexfile.js
@@ -1,11 +1,55 @@
|
|||||||
|
// knexfile.js
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
|
function must(name) {
|
||||||
|
const v = process.env[name];
|
||||||
|
if (!v) throw new Error(`${name} não definido no .env`);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shared = {
|
||||||
|
client: 'pg',
|
||||||
|
migrations: {
|
||||||
|
directory: './src/db/migrations',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildConnectionFromEnv() {
|
||||||
|
// Se houver DATABASE_URL, usa-o.
|
||||||
|
// Se PGSSL=true, aplica ssl no formato esperado pelo driver pg (dentro de connection).
|
||||||
|
const ssl =
|
||||||
|
process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined;
|
||||||
|
|
||||||
|
if (process.env.DATABASE_URL) {
|
||||||
|
// knex aceita string, mas o ssl precisa estar no objeto:
|
||||||
|
return ssl
|
||||||
|
? { connectionString: process.env.DATABASE_URL, ssl }
|
||||||
|
: process.env.DATABASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback para vars soltas
|
||||||
|
return {
|
||||||
|
host: process.env.PGHOST || '127.0.0.1',
|
||||||
|
port: Number(process.env.PGPORT || 5432),
|
||||||
|
user: process.env.PGUSER || 'postgres',
|
||||||
|
password: process.env.PGPASSWORD || 'postgres',
|
||||||
|
database: process.env.PGDATABASE || 'evse',
|
||||||
|
...(ssl ? { ssl } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
development: {
|
development: {
|
||||||
client: 'pg',
|
...shared,
|
||||||
connection: process.env.DATABASE_URL,
|
connection: buildConnectionFromEnv(),
|
||||||
migrations: {
|
|
||||||
directory: './migrations',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
production: {
|
||||||
|
...shared,
|
||||||
|
// Em produção normalmente queres obrigar DATABASE_URL (se for o teu caso):
|
||||||
|
// connection: must('DATABASE_URL'),
|
||||||
|
// Mas mantendo compatível com vars soltas:
|
||||||
|
connection: buildConnectionFromEnv(),
|
||||||
|
pool: { min: 2, max: 10 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
const jwt = require('jsonwebtoken');
|
|
||||||
|
|
||||||
if (!process.env.JWT_SECRET) {
|
|
||||||
throw new Error('JWT_SECRET não definido no .env');
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifyToken(req, res, next) {
|
|
||||||
const authHeader =
|
|
||||||
req.headers['authorization'] || req.headers['Authorization'];
|
|
||||||
|
|
||||||
if (!authHeader) {
|
|
||||||
return res.status(403).json({ error: 'Token não fornecido' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
||||||
if (!match) {
|
|
||||||
return res
|
|
||||||
.status(403)
|
|
||||||
.json({ error: 'Token malformado. Use "Bearer <token>"' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = match[1];
|
|
||||||
|
|
||||||
jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
|
|
||||||
if (err) {
|
|
||||||
if (err.name === 'TokenExpiredError') {
|
|
||||||
return res.status(403).json({ error: 'Sessão expirada' });
|
|
||||||
}
|
|
||||||
return res.status(403).json({ error: 'Token inválido' });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!payload?.id) {
|
|
||||||
return res.status(403).json({ error: 'Token inválido' });
|
|
||||||
}
|
|
||||||
|
|
||||||
req.user = payload;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = verifyToken;
|
|
||||||
59
migrate_structure.sh
Executable file
59
migrate_structure.sh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "==> 0) Criar branch (opcional)"
|
||||||
|
# git checkout -b refactor/estrutura-src
|
||||||
|
|
||||||
|
echo "==> 1) Criar pastas da nova estrutura"
|
||||||
|
mkdir -p src/{config,db,repositories,services,domain/normalize,mqtt/handlers,middleware,routes}
|
||||||
|
mkdir -p src/db/migrations
|
||||||
|
|
||||||
|
echo "==> 2) Mover ficheiros principais"
|
||||||
|
# app/server
|
||||||
|
mv app.js src/app.js
|
||||||
|
mv server.js src/server.js
|
||||||
|
|
||||||
|
# db
|
||||||
|
mv db.js src/db/knex.js
|
||||||
|
|
||||||
|
# knexfile continua na raiz (ok)
|
||||||
|
|
||||||
|
echo "==> 3) Mover routes"
|
||||||
|
mv routes/chargers.js src/routes/chargers.routes.js
|
||||||
|
mv routes/charger_sessions.js src/routes/sessions.routes.js
|
||||||
|
mv routes/push.js src/routes/push.routes.js
|
||||||
|
mv routes/users.js src/routes/users.routes.js
|
||||||
|
|
||||||
|
echo "==> 4) Mover middleware"
|
||||||
|
mv middleware/verifyToken.js src/middleware/auth.js
|
||||||
|
|
||||||
|
echo "==> 5) Mover MQTT"
|
||||||
|
mv mqtt/client.js src/mqtt/index.js
|
||||||
|
|
||||||
|
echo "==> 6) Mover pushService (utils -> services)"
|
||||||
|
mv utils/pushService.js src/services/push.service.js
|
||||||
|
|
||||||
|
echo "==> 7) Mover migrations"
|
||||||
|
mv migrations/* src/db/migrations/
|
||||||
|
|
||||||
|
echo "==> 8) Limpar pastas antigas (se vazias)"
|
||||||
|
rmdir routes 2>/dev/null || true
|
||||||
|
rmdir middleware 2>/dev/null || true
|
||||||
|
rmdir mqtt 2>/dev/null || true
|
||||||
|
rmdir utils 2>/dev/null || true
|
||||||
|
rmdir migrations 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "==> 9) Criar placeholders úteis (opcional)"
|
||||||
|
touch src/config/index.js
|
||||||
|
touch src/services/{chargers.service.js,configs.service.js,sessions.service.js}
|
||||||
|
touch src/repositories/{chargers.repo.js,sessions.repo.js,push.repo.js}
|
||||||
|
touch src/domain/normalize/{chargingStatus.js,chargingConfig.js}
|
||||||
|
touch src/middleware/{validate.js,errorHandler.js}
|
||||||
|
touch src/mqtt/publishers.js
|
||||||
|
touch src/mqtt/handlers/{evse.handler.js,meter.handler.js,legacy.handler.js}
|
||||||
|
|
||||||
|
echo "==> 10) Mostrar nova árvore (até 4 níveis)"
|
||||||
|
tree -a -L 4 src || true
|
||||||
|
|
||||||
|
echo "✅ Estrutura criada e ficheiros movidos."
|
||||||
|
echo "⚠️ Próximo passo: corrigir os imports/paths (vai quebrar até ajustar)."
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
// migrations/20250619_create_tables.js
|
|
||||||
exports.up = async function(knex) {
|
|
||||||
// Create 'users' table
|
|
||||||
await knex.schema.createTable('users', (table) => {
|
|
||||||
table.increments('id').primary();
|
|
||||||
table.string('username', 255).notNullable().unique();
|
|
||||||
table.string('password', 255).notNullable();
|
|
||||||
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create 'chargers' table with new fields
|
|
||||||
await knex.schema.createTable('chargers', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.integer('user_id').unsigned().notNullable()
|
|
||||||
.references('id').inTable('users').onDelete('CASCADE');
|
|
||||||
table.string('location', 255).notNullable();
|
|
||||||
table.string('status', 50).notNullable().defaultTo('offline');
|
|
||||||
table.integer('charging_current').notNullable().defaultTo(32);
|
|
||||||
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
|
|
||||||
table.string('mqtt_user', 255).notNullable();
|
|
||||||
table.string('mqtt_pass', 255).notNullable();
|
|
||||||
table.string('mqtt_topic', 255).notNullable().unique();
|
|
||||||
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
||||||
|
|
||||||
// Add power and current for 3 phases, voltage and other new fields
|
|
||||||
table.integer('charging_time').notNullable().defaultTo(0); // Total charging time
|
|
||||||
table.decimal('consumption', 8, 2).notNullable().defaultTo(0); // Consumption (kWh)
|
|
||||||
|
|
||||||
// Power for 3 phases (L1, L2, L3)
|
|
||||||
table.decimal('power_l1', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('power_l2', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('power_l3', 8, 2).notNullable().defaultTo(0);
|
|
||||||
|
|
||||||
// Voltage for 3 phases (L1, L2, L3)
|
|
||||||
table.decimal('voltage_l1', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('voltage_l2', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('voltage_l3', 8, 2).notNullable().defaultTo(0);
|
|
||||||
|
|
||||||
// Current for 3 phases (L1, L2, L3)
|
|
||||||
table.decimal('current_l1', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('current_l2', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('current_l3', 8, 2).notNullable().defaultTo(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create 'charger_configs' table
|
|
||||||
await knex.schema.createTable('charger_configs', (table) => {
|
|
||||||
table.uuid('charger_id').primary()
|
|
||||||
.references('id').inTable('chargers').onDelete('CASCADE');
|
|
||||||
table.integer('max_charging_current').notNullable().defaultTo(32);
|
|
||||||
table.boolean('require_auth').notNullable().defaultTo(false);
|
|
||||||
table.boolean('rcm_enabled').notNullable().defaultTo(false);
|
|
||||||
table.integer('temperature_limit').notNullable().defaultTo(60);
|
|
||||||
table.timestamp('config_received_at').notNullable().defaultTo(knex.fn.now());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create 'charger_sessions' table
|
|
||||||
await knex.schema.createTable('charger_sessions', (table) => {
|
|
||||||
table.increments('id').primary();
|
|
||||||
table.uuid('charger_id').notNullable()
|
|
||||||
.references('id').inTable('chargers').onDelete('CASCADE');
|
|
||||||
table.timestamp('started_at').notNullable();
|
|
||||||
table.timestamp('ended_at');
|
|
||||||
table.decimal('kwh', 8, 2).notNullable().defaultTo(0);
|
|
||||||
table.decimal('cost', 10, 2);
|
|
||||||
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = async function(knex) {
|
|
||||||
await knex.schema.dropTableIfExists('charger_sessions');
|
|
||||||
await knex.schema.dropTableIfExists('charger_configs');
|
|
||||||
await knex.schema.dropTableIfExists('chargers');
|
|
||||||
await knex.schema.dropTableIfExists('users');
|
|
||||||
};
|
|
||||||
436
mqtt/client.js
436
mqtt/client.js
@@ -1,436 +0,0 @@
|
|||||||
// mqtt/client.js
|
|
||||||
const mqtt = require('mqtt');
|
|
||||||
const EventEmitter = require('events');
|
|
||||||
const db = require('../db');
|
|
||||||
const { sendPushToUser } = require('../utils/pushService');
|
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
const emitter = new EventEmitter();
|
|
||||||
|
|
||||||
const MQTT_URL = process.env.MQTT_URL || 'mqtt://localhost:1883';
|
|
||||||
const mqttUser = process.env.MQTT_USER || 'admin';
|
|
||||||
const mqttPass = process.env.MQTT_PASS || '123QWEasd';
|
|
||||||
|
|
||||||
const client = mqtt.connect(MQTT_URL, {
|
|
||||||
username: mqttUser,
|
|
||||||
password: mqttPass,
|
|
||||||
reconnectPeriod: 2000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// Helpers
|
|
||||||
// --------------------
|
|
||||||
const lastEnabled = {}; // por chargerId
|
|
||||||
|
|
||||||
function getStatusFromStateCode(code) {
|
|
||||||
const map = {
|
|
||||||
A1: '🔌 Not Conn.',
|
|
||||||
B1: '🟡 Unauth.',
|
|
||||||
B2: '🟢 Ready',
|
|
||||||
C1: '⚡ Wait',
|
|
||||||
C2: '⚡ Charging',
|
|
||||||
D1: '💨 Vent (req)',
|
|
||||||
D2: '💨 Vent',
|
|
||||||
E: '❌ CP Error',
|
|
||||||
F: '⚠️ Fault',
|
|
||||||
};
|
|
||||||
return map[code] || '❓ Unknown';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTriple(arr) {
|
|
||||||
return Array.isArray(arr)
|
|
||||||
? arr.slice(0, 3).map((n) => Math.round((Number(n) || 0) * 10) / 10)
|
|
||||||
: [0, 0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function toOneDecimal(v) {
|
|
||||||
const n = Number(v);
|
|
||||||
if (!Number.isFinite(n)) return 0;
|
|
||||||
return Math.round(n * 10) / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
function shallowEqual(a, b) {
|
|
||||||
if (a === b) return true;
|
|
||||||
if (!a || !b) return false;
|
|
||||||
const ak = Object.keys(a);
|
|
||||||
const bk = Object.keys(b);
|
|
||||||
if (ak.length !== bk.length) return false;
|
|
||||||
for (const k of ak) {
|
|
||||||
const av = a[k];
|
|
||||||
const bv = b[k];
|
|
||||||
if (Array.isArray(av) || Array.isArray(bv)) {
|
|
||||||
if (!Array.isArray(av) || !Array.isArray(bv)) return false;
|
|
||||||
if (av.length !== bv.length) return false;
|
|
||||||
for (let i = 0; i < av.length; i++) {
|
|
||||||
if (av[i] !== bv[i]) return false;
|
|
||||||
}
|
|
||||||
} else if (av !== bv) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// Caches
|
|
||||||
// --------------------
|
|
||||||
|
|
||||||
// último estado normalizado por chargerId
|
|
||||||
const lastStateByChargerId = new Map(); // chargerId -> normalizedState
|
|
||||||
|
|
||||||
// cache charger por mqttTopic (evita SELECT a cada msg)
|
|
||||||
const chargerCache = new Map(); // mqttTopic -> { charger, fetchedAt }
|
|
||||||
const CHARGER_CACHE_TTL_MS = Number(process.env.CHARGER_CACHE_TTL_MS || 30000);
|
|
||||||
|
|
||||||
async function getChargerByMqttTopic(mqttTopic) {
|
|
||||||
const cached = chargerCache.get(mqttTopic);
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
if (cached && now - cached.fetchedAt < CHARGER_CACHE_TTL_MS) {
|
|
||||||
return cached.charger;
|
|
||||||
}
|
|
||||||
|
|
||||||
const charger = await db('chargers').where({ mqtt_topic: mqttTopic }).first();
|
|
||||||
if (charger) {
|
|
||||||
chargerCache.set(mqttTopic, { charger, fetchedAt: now });
|
|
||||||
}
|
|
||||||
return charger;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// Subscribe
|
|
||||||
// --------------------
|
|
||||||
client.on('connect', () => {
|
|
||||||
console.log('[MQTT] Conectado ao broker:', MQTT_URL);
|
|
||||||
|
|
||||||
// ✅ tópicos fixos que TU pediste manter
|
|
||||||
const fixedTopics = ['+/state', '+/response/config/evse'];
|
|
||||||
|
|
||||||
// opcional via env (mas não substitui os fixos)
|
|
||||||
const envTopics = (process.env.MQTT_SUB_TOPICS || '')
|
|
||||||
.split(',')
|
|
||||||
.map((s) => s.trim())
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const topicsToSub = [...new Set([...fixedTopics, ...envTopics])];
|
|
||||||
|
|
||||||
topicsToSub.forEach((t) => {
|
|
||||||
client.subscribe(t, { qos: 0 }, (err, granted) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('[MQTT] Falha ao subscrever', t, err.message);
|
|
||||||
} else {
|
|
||||||
console.log('[MQTT] Subscrito:', granted?.map((g) => g.topic).join(', ') || t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// Messages
|
|
||||||
// --------------------
|
|
||||||
client.on('message', async (topic, message) => {
|
|
||||||
// LOG para garantir que está a consumir
|
|
||||||
console.log('[MQTT] msg recebida em:', topic);
|
|
||||||
|
|
||||||
const parts = topic.split('/');
|
|
||||||
const mqttTopic = parts[0]; // ex: bf92842c365a
|
|
||||||
const subtopic = parts.slice(1).join('/'); // ex: state ou response/config/evse
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// STATE
|
|
||||||
// --------------------
|
|
||||||
if (subtopic === 'state') {
|
|
||||||
try {
|
|
||||||
const payload = JSON.parse(message.toString());
|
|
||||||
|
|
||||||
const charger = await getChargerByMqttTopic(mqttTopic);
|
|
||||||
if (!charger) {
|
|
||||||
console.warn(`[MQTT] Charger não encontrado para topic: ${mqttTopic}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chargerId = charger.id;
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
const stateCode = String(payload.state || '').split(' ')[0];
|
|
||||||
if (!stateCode) {
|
|
||||||
console.warn(`[MQTT] Estado ausente/inválido para charger ID ${chargerId}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const status = getStatusFromStateCode(stateCode);
|
|
||||||
|
|
||||||
const [p1, p2, p3] = getTriple(payload.power);
|
|
||||||
const [v1, v2, v3] = getTriple(payload.voltage);
|
|
||||||
const [c1, c2, c3] = getTriple(payload.current);
|
|
||||||
|
|
||||||
const consumption = toOneDecimal(payload.consumption);
|
|
||||||
const chargingTime = toOneDecimal(
|
|
||||||
payload.chargingTime ?? payload.sessionTime
|
|
||||||
);
|
|
||||||
|
|
||||||
const normalizedState = {
|
|
||||||
status,
|
|
||||||
charging_current: c1,
|
|
||||||
consumption,
|
|
||||||
charging_time: chargingTime,
|
|
||||||
|
|
||||||
power_l1: p1,
|
|
||||||
power_l2: p2,
|
|
||||||
power_l3: p3,
|
|
||||||
|
|
||||||
voltage_l1: v1,
|
|
||||||
voltage_l2: v2,
|
|
||||||
voltage_l3: v3,
|
|
||||||
|
|
||||||
current_l1: c1,
|
|
||||||
current_l2: c2,
|
|
||||||
current_l3: c3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevState = lastStateByChargerId.get(chargerId);
|
|
||||||
const changed = !prevState || !shallowEqual(prevState, normalizedState);
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
await db('chargers')
|
|
||||||
.where({ id: chargerId })
|
|
||||||
.update({
|
|
||||||
...normalizedState,
|
|
||||||
updated_at: now.toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
lastStateByChargerId.set(chargerId, normalizedState);
|
|
||||||
console.log(`[DB] Estado atualizado para charger ID ${chargerId}`);
|
|
||||||
} else {
|
|
||||||
// só para debug — podes remover depois
|
|
||||||
console.log(`[MQTT] Estado repetido, sem write (charger ${chargerId})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sessões start/stop (mantido)
|
|
||||||
const previouslyEnabled = lastEnabled[chargerId] || false;
|
|
||||||
const currentlyEnabled = stateCode === 'C2';
|
|
||||||
|
|
||||||
if (!previouslyEnabled && currentlyEnabled) {
|
|
||||||
const activeSession = await db('charger_sessions')
|
|
||||||
.where({ charger_id: chargerId })
|
|
||||||
.whereNull('ended_at')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (!activeSession) {
|
|
||||||
await db('charger_sessions').insert({
|
|
||||||
charger_id: chargerId,
|
|
||||||
started_at: now,
|
|
||||||
kwh: consumption,
|
|
||||||
});
|
|
||||||
console.log(`[DB] Sessão iniciada para charger ID ${chargerId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previouslyEnabled && !currentlyEnabled) {
|
|
||||||
const session = await db('charger_sessions')
|
|
||||||
.where({ charger_id: chargerId })
|
|
||||||
.whereNull('ended_at')
|
|
||||||
.first();
|
|
||||||
|
|
||||||
if (session) {
|
|
||||||
await db('charger_sessions')
|
|
||||||
.where({ id: session.id })
|
|
||||||
.update({ ended_at: now, kwh: consumption });
|
|
||||||
|
|
||||||
console.log(`[DB] Sessão finalizada para charger ID ${chargerId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastEnabled[chargerId] = currentlyEnabled;
|
|
||||||
|
|
||||||
// emit para socket
|
|
||||||
emitter.emit('charging-status', {
|
|
||||||
charger_id: chargerId,
|
|
||||||
mqtt_topic: mqttTopic,
|
|
||||||
status,
|
|
||||||
stateCode,
|
|
||||||
consumption,
|
|
||||||
chargingTime,
|
|
||||||
power: [p1, p2, p3],
|
|
||||||
voltage: [v1, v2, v3],
|
|
||||||
current: [c1, c2, c3],
|
|
||||||
raw: payload,
|
|
||||||
});
|
|
||||||
|
|
||||||
// push (mantido)
|
|
||||||
if (status === '⚠️ Fault' || status === '❌ CP Error') {
|
|
||||||
await sendPushToUser(charger.user_id, {
|
|
||||||
title: '⚠️ Erro no carregador',
|
|
||||||
body: `${charger.location || 'Carregador'} entrou em falha.`,
|
|
||||||
url: `/charger/${charger.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previouslyEnabled && !currentlyEnabled) {
|
|
||||||
await sendPushToUser(charger.user_id, {
|
|
||||||
title: '✅ Carregamento concluído',
|
|
||||||
body: `${charger.location || 'Carregador'} terminou o carregamento.`,
|
|
||||||
url: `/history`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`[MQTT] Erro ao processar state '${mqttTopic}':`, err);
|
|
||||||
console.error('Payload recebido:', message.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// CONFIG RESPONSE
|
|
||||||
// --------------------
|
|
||||||
else if (subtopic === 'response/config/evse') {
|
|
||||||
try {
|
|
||||||
const payload = JSON.parse(message.toString());
|
|
||||||
|
|
||||||
const charger = await getChargerByMqttTopic(mqttTopic);
|
|
||||||
if (!charger) {
|
|
||||||
console.warn(`[MQTT] Charger não encontrado para topic: ${mqttTopic}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const configData = {
|
|
||||||
charger_id: charger.id,
|
|
||||||
max_charging_current: payload.maxChargingCurrent || 32,
|
|
||||||
require_auth: !!payload.requireAuth,
|
|
||||||
rcm_enabled: !!payload.rcm,
|
|
||||||
temperature_limit: payload.temperatureThreshold || 60,
|
|
||||||
config_received_at: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const existingConfig = await db('charger_configs')
|
|
||||||
.where({ charger_id: charger.id })
|
|
||||||
.first();
|
|
||||||
|
|
||||||
const prevCfgNorm = existingConfig
|
|
||||||
? {
|
|
||||||
max_charging_current: Number(existingConfig.max_charging_current) || 32,
|
|
||||||
require_auth: !!existingConfig.require_auth,
|
|
||||||
rcm_enabled: !!existingConfig.rcm_enabled,
|
|
||||||
temperature_limit: Number(existingConfig.temperature_limit) || 60,
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const nextCfgNorm = {
|
|
||||||
max_charging_current: Number(configData.max_charging_current) || 32,
|
|
||||||
require_auth: !!configData.require_auth,
|
|
||||||
rcm_enabled: !!configData.rcm_enabled,
|
|
||||||
temperature_limit: Number(configData.temperature_limit) || 60,
|
|
||||||
};
|
|
||||||
|
|
||||||
const cfgChanged = !prevCfgNorm || !shallowEqual(prevCfgNorm, nextCfgNorm);
|
|
||||||
|
|
||||||
if (cfgChanged) {
|
|
||||||
if (existingConfig) {
|
|
||||||
await db('charger_configs')
|
|
||||||
.where({ charger_id: charger.id })
|
|
||||||
.update(configData);
|
|
||||||
console.log(`[DB] Configuração atualizada para charger ID ${charger.id}`);
|
|
||||||
} else {
|
|
||||||
await db('charger_configs').insert(configData);
|
|
||||||
console.log(`[DB] Nova configuração inserida para charger ID ${charger.id}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(`[MQTT] Config repetida, sem write (charger ${charger.id})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter.emit('charging-config', {
|
|
||||||
...configData,
|
|
||||||
mqtt_topic: mqttTopic,
|
|
||||||
raw: payload,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`[MQTT] Erro ao processar config '${mqttTopic}':`, err);
|
|
||||||
console.error('Payload recebido:', message.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// Broker offline / checker (igual ao teu)
|
|
||||||
// --------------------
|
|
||||||
client.on('offline', async () => {
|
|
||||||
console.warn('[MQTT] Broker offline');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const chargers = await db('chargers').select('id', 'user_id', 'location');
|
|
||||||
const uniqueUsers = [...new Set(chargers.map((c) => c.user_id))];
|
|
||||||
|
|
||||||
await Promise.allSettled(
|
|
||||||
uniqueUsers.map((userId) =>
|
|
||||||
sendPushToUser(userId, {
|
|
||||||
title: '📡 Broker MQTT offline',
|
|
||||||
body: 'O sistema perdeu ligação ao broker. Alguns estados podem estar desatualizados.',
|
|
||||||
url: '/',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('[MQTT] erro offline push:', err.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setInterval(async () => {
|
|
||||||
try {
|
|
||||||
const timeoutMinutes = Number(process.env.CHARGER_OFFLINE_MINUTES || 5);
|
|
||||||
const limitDate = new Date(Date.now() - timeoutMinutes * 60 * 1000);
|
|
||||||
|
|
||||||
const offlineChargers = await db('chargers')
|
|
||||||
.where('updated_at', '<', limitDate.toISOString())
|
|
||||||
.andWhereNot({ status: 'offline' })
|
|
||||||
.select('*');
|
|
||||||
|
|
||||||
for (const ch of offlineChargers) {
|
|
||||||
await db('chargers')
|
|
||||||
.where({ id: ch.id })
|
|
||||||
.update({ status: 'offline' });
|
|
||||||
|
|
||||||
lastStateByChargerId.delete(ch.id);
|
|
||||||
lastEnabled[ch.id] = false;
|
|
||||||
|
|
||||||
await sendPushToUser(ch.user_id, {
|
|
||||||
title: '🔌 Carregador offline',
|
|
||||||
body: `${ch.location || 'Carregador'} está offline há mais de ${timeoutMinutes} min.`,
|
|
||||||
url: `/charger/${ch.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('[MQTT] offline checker erro:', err.message);
|
|
||||||
}
|
|
||||||
}, 60 * 1000);
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// API pública
|
|
||||||
// --------------------
|
|
||||||
function on(event, handler) {
|
|
||||||
emitter.on(event, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendConfig(chargerTopic, property, value) {
|
|
||||||
const payload = { [property]: value };
|
|
||||||
client.publish(`${chargerTopic}/set/config/evse`, JSON.stringify(payload), {
|
|
||||||
qos: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendEnable(chargerTopic, enable) {
|
|
||||||
client.publish(
|
|
||||||
`${chargerTopic}/enable`,
|
|
||||||
JSON.stringify({ enable: !!enable }),
|
|
||||||
{ qos: 1 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestConfig(chargerTopic) {
|
|
||||||
client.publish(`${chargerTopic}/request/config/evse`, null, { qos: 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
on,
|
|
||||||
sendConfig,
|
|
||||||
sendEnable,
|
|
||||||
requestConfig,
|
|
||||||
};
|
|
||||||
0
node_modules/@socket.io/component-emitter/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/Readme.md
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/Readme.md
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/index.js
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/index.js
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/cjs/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/index.js
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/index.js
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/lib/esm/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@socket.io/component-emitter/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/README.md
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/README.md
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/package.json
generated
vendored
Normal file → Executable file
0
node_modules/@types/cors/package.json
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/README.md
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/README.md
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.d.ts.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.d.ts.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.js
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.js
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.js.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/helpers.js.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.d.ts.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.d.ts.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.js
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.js
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.js.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/dist/index.js.map
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/package.json
generated
vendored
Normal file → Executable file
0
node_modules/agent-base/package.json
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/.eslintrc.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/.eslintrc.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/README.md
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/README.md
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/api.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/api.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/buffer.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/buffer.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/node.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/node.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/reporter.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/base/reporter.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/constants/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/constants/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/constants/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/constants/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/pem.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/decoders/pem.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/der.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/pem.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/lib/asn1/encoders/pem.js
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/package.json
generated
vendored
Normal file → Executable file
0
node_modules/asn1.js/package.json
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/README.md
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/README.md
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/bench.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/bench.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/index.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/abort.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/abort.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/async.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/async.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/defer.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/defer.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/iterate.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/iterate.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_asynckit.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_asynckit.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_parallel.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_parallel.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_serial.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_serial.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_serial_ordered.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/readable_serial_ordered.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/state.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/state.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/streamify.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/streamify.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/terminator.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/lib/terminator.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/package.json
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/package.json
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/parallel.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/parallel.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/serial.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/serial.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/serialOrdered.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/serialOrdered.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/stream.js
generated
vendored
Normal file → Executable file
0
node_modules/asynckit/stream.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/CHANGELOG.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/CHANGELOG.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/axios/LICENSE
generated
vendored
Normal file → Executable file
0
node_modules/axios/MIGRATION_GUIDE.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/MIGRATION_GUIDE.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/README.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/README.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.min.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.min.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.min.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/axios.min.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/browser/axios.cjs
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/browser/axios.cjs
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/browser/axios.cjs.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/browser/axios.cjs.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.min.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.min.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.min.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/esm/axios.min.js.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/node/axios.cjs
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/node/axios.cjs
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/node/axios.cjs.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/dist/node/axios.cjs.map
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.d.cts
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.d.cts
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.d.ts
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/index.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/README.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/README.md
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/adapters.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/adapters.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/fetch.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/fetch.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/xhr.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/adapters/xhr.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/CancelToken.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/CancelToken.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/CanceledError.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/CanceledError.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/isCancel.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/cancel/isCancel.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/Axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/Axios.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/AxiosError.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/AxiosError.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/AxiosHeaders.js
generated
vendored
Normal file → Executable file
0
node_modules/axios/lib/core/AxiosHeaders.js
generated
vendored
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user