first commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
const mysql = require('mysql2');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const pool = mysql.createPool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
});
|
||||
|
||||
// Convert pool to allow async/await
|
||||
const promisePool = pool.promise();
|
||||
|
||||
console.log(`Intentando conectar a DB: ${process.env.DB_NAME} en ${process.env.DB_HOST}`);
|
||||
|
||||
module.exports = promisePool;
|
||||
@@ -0,0 +1,33 @@
|
||||
const admin = require('firebase-admin');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const initFirebaseAdmin = () => {
|
||||
if (admin.apps.length) {
|
||||
return admin;
|
||||
}
|
||||
|
||||
const credentialPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
||||
if (!credentialPath) {
|
||||
throw new Error(
|
||||
'GOOGLE_APPLICATION_CREDENTIALS no está definida. Configúrala antes de inicializar Firebase Admin.'
|
||||
);
|
||||
}
|
||||
|
||||
const options = {
|
||||
credential: admin.credential.applicationDefault()
|
||||
};
|
||||
|
||||
if (process.env.FIREBASE_DATABASE_URL) {
|
||||
options.databaseURL = process.env.FIREBASE_DATABASE_URL;
|
||||
}
|
||||
|
||||
admin.initializeApp(options);
|
||||
return admin;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
admin,
|
||||
initFirebaseAdmin
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
const db = require('../config/db');
|
||||
const md5 = require('md5');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const login = async (req, res) => {
|
||||
try {
|
||||
const { usuario, contrasena } = req.body;
|
||||
|
||||
if (!usuario || !contrasena) {
|
||||
return res.json("0"); // Maintain legacy error response
|
||||
}
|
||||
|
||||
const hashedPassword = md5(contrasena);
|
||||
|
||||
// Parametrized query to prevent SQL Injection
|
||||
const [rows] = await db.query(
|
||||
"SELECT id_transportista, nombre, id_proveedor, dni FROM m_proveedores_trasportistas WHERE desactivado = 0 AND dni = ? AND password = ?",
|
||||
[usuario, hashedPassword]
|
||||
);
|
||||
|
||||
const fecha = new Date().toISOString().slice(0, 19).replace('T', ' '); // Format: YYYY-MM-DD HH:mm:ss
|
||||
|
||||
if (rows.length > 0) {
|
||||
const user = rows[0];
|
||||
|
||||
// Log successful attempt
|
||||
await db.query(
|
||||
"INSERT INTO c_log_app (usuario, fecha, acceso) VALUES (?, ?, ?)",
|
||||
[usuario, fecha, 1]
|
||||
);
|
||||
|
||||
// Legacy format response: array with [dni, id_proveedor]
|
||||
// const response = [user.dni, user.id_proveedor];
|
||||
|
||||
// Modern JWT response
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: user.id_transportista,
|
||||
dni: user.dni,
|
||||
id_proveedor: user.id_proveedor
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '365d' } // Long expiration for app usability
|
||||
);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
token: token,
|
||||
user: {
|
||||
dni: user.dni,
|
||||
id_proveedor: user.id_proveedor,
|
||||
nombre: user.nombre
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Log failed attempt
|
||||
await db.query(
|
||||
"INSERT INTO c_log_app (usuario, fecha, acceso) VALUES (?, ?, ?)",
|
||||
[usuario, fecha, 0]
|
||||
);
|
||||
|
||||
return res.json("0");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Login error:", error);
|
||||
return res.status(500).json("0"); // Fallback to "0" on error to not crash client
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
login
|
||||
};
|
||||
@@ -0,0 +1,118 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const saveLocation = async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
console.log("🔍 [DEBUG SERVER] Full Request Body:", JSON.stringify(data, null, 2)); // Debug payload structure
|
||||
const logFile = path.join(__dirname, '../logs/coordenadas.txt');
|
||||
|
||||
// Ensure logs directory exists
|
||||
const logDir = path.dirname(logFile);
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Helper function to process a single location object
|
||||
const processSingleLocation = (locationData) => {
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
||||
|
||||
// Intentar obtener alias desde "extras.alias" o "alias"
|
||||
let alias = 'Desconocido';
|
||||
if (locationData.extras && locationData.extras.alias) {
|
||||
alias = locationData.extras.alias;
|
||||
} else if (locationData.alias) {
|
||||
alias = locationData.alias;
|
||||
}
|
||||
|
||||
let lat, lng;
|
||||
if (locationData.location && locationData.location.coords) {
|
||||
lat = locationData.location.coords.latitude;
|
||||
lng = locationData.location.coords.longitude;
|
||||
} else if (locationData.coords) {
|
||||
lat = locationData.coords.latitude;
|
||||
lng = locationData.coords.longitude;
|
||||
} else if (locationData.latitude && locationData.longitude) {
|
||||
lat = locationData.latitude;
|
||||
lng = locationData.longitude;
|
||||
}
|
||||
|
||||
if (lat !== undefined && lng !== undefined) {
|
||||
console.log(`📍 [Server] Received: Alias=${alias} | Lat=${lat} | Lng=${lng}`);
|
||||
const linea = `[${timestamp}] Alias: ${alias} | IP: ${ip} | Lat: ${lat} | Lng: ${lng}\n`;
|
||||
fs.appendFileSync(logFile, linea);
|
||||
return true;
|
||||
} else {
|
||||
const errorLine = `[${timestamp}] Alias: ${alias} | IP: ${ip} | ⚠️ Formato inválido o sin coordenadas\n`;
|
||||
try {
|
||||
fs.appendFileSync(logFile, errorLine);
|
||||
} catch (e) {
|
||||
console.error("Error writing error log:", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Determine if it's a Batch (Array) or Single Location
|
||||
let locations = [];
|
||||
let globalAlias = null;
|
||||
|
||||
// 1. Priority: JWT User (Secure & Reliable)
|
||||
if (req.user) {
|
||||
console.log("🔐 [Server] req.user payload:", JSON.stringify(req.user)); // Debug token content
|
||||
if (req.user.dni) {
|
||||
globalAlias = req.user.dni;
|
||||
console.log(`🔒 [Server] Authenticated User (GlobalAlias): ${globalAlias}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Case 1: Root Array
|
||||
if (Array.isArray(data)) {
|
||||
locations = data;
|
||||
}
|
||||
// Case 2: Wrapped in "location" property (common with params)
|
||||
else if (data.location && Array.isArray(data.location)) {
|
||||
locations = data.location;
|
||||
// Check for alias in root (Fallback if no token)
|
||||
if (!globalAlias) {
|
||||
if (data.alias) globalAlias = data.alias;
|
||||
else if (data.params && data.params.alias) globalAlias = data.params.alias;
|
||||
}
|
||||
}
|
||||
// Case 3: Single Object
|
||||
else {
|
||||
locations = [data];
|
||||
}
|
||||
|
||||
console.log(`📦 [Server] Processing ${locations.length} locations`);
|
||||
|
||||
let savedCount = 0;
|
||||
locations.forEach(loc => {
|
||||
// FORCE Inject global alias (Security: Always trust Token over Client Body)
|
||||
if (globalAlias) {
|
||||
if (!loc.extras) loc.extras = {};
|
||||
loc.extras.alias = globalAlias;
|
||||
// Also purge root alias to avoid confusion in processSingleLocation logic
|
||||
if (loc.alias) delete loc.alias;
|
||||
}
|
||||
// Fallback for processSingleLocation helper logic...
|
||||
|
||||
if (processSingleLocation(loc)) savedCount++;
|
||||
});
|
||||
|
||||
if (savedCount > 0) {
|
||||
return res.json({ success: true, count: savedCount, message: "Locations saved" });
|
||||
} else {
|
||||
return res.json({ success: false, error: 'no_coords', message: "No valid coordinates found in batch/object" });
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error saving location:", error);
|
||||
return res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
saveLocation
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
const db = require('../config/db');
|
||||
const md5 = require('md5');
|
||||
|
||||
const getProfile = async (req, res) => {
|
||||
try {
|
||||
// Handle potential mismatch from legacy code (id_prov vs id_proveedor)
|
||||
const id_proveedor = req.body.id_proveedor || req.body.id_prov;
|
||||
|
||||
if (!id_proveedor) {
|
||||
return res.status(400).json({ error: "Missing id_proveedor" });
|
||||
}
|
||||
|
||||
const [rows] = await db.query(
|
||||
"SELECT nombre, num_proveedor, cp FROM m_proveedores WHERE id_proveedor = ?",
|
||||
[id_proveedor]
|
||||
);
|
||||
|
||||
// PHP returned an array of objects
|
||||
return res.json(rows);
|
||||
} catch (error) {
|
||||
console.error("Error getting profile:", error);
|
||||
return res.status(500).json([]);
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = async (req, res) => {
|
||||
try {
|
||||
const { usuario, password } = req.body;
|
||||
|
||||
if (!usuario || !password) {
|
||||
return res.json("1"); // Error code
|
||||
}
|
||||
|
||||
const hashedPassword = md5(password);
|
||||
|
||||
const [result] = await db.query(
|
||||
"UPDATE m_proveedores_trasportistas SET password = ? WHERE dni = ?",
|
||||
[hashedPassword, usuario]
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return res.json("0"); // Success code
|
||||
} else {
|
||||
// Could mean user not found or password didn't change, but legacy returns 0 for success.
|
||||
// If nothing updated, maybe user doesn't exist.
|
||||
// Legacy logic: if query runs, return 0. If error, return 1.
|
||||
// But checking affectedRows is safer.
|
||||
// Let's stick to legacy behavior: if no error, return 0?
|
||||
// The PHP code only returns 1 if mysqli_query fails (returns false).
|
||||
return res.json("0");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error changing password:", error);
|
||||
return res.json("1");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getProfile,
|
||||
changePassword
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
const db = require('../config/db');
|
||||
|
||||
const recordTest = async (req, res) => {
|
||||
try {
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
|
||||
|
||||
console.log(`🔥 [Stress Test] Request received from ${ip}`);
|
||||
|
||||
// Insertar en prueba_esfuerzo
|
||||
// Asume columnas 'fecha' (datetime) e 'ip' (varchar)
|
||||
const [result] = await db.query("INSERT INTO prueba_esfuerzo (fecha, ip) VALUES (NOW(), ?)", [ip]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Insertado en prueba_esfuerzo",
|
||||
insertId: result.insertId,
|
||||
ip: ip
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Stress test error:", error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { recordTest };
|
||||
Executable
+8994
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const authenticateDevice = (req, res, next) => {
|
||||
// 1. Obtener Token del Header 'Authorization: Bearer <token>'
|
||||
const authHeader = req.headers.authorization;
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Ignorar 'Bearer '
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({ success: false, error: 'MISSING_TOKEN', message: 'Token requerido' });
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. Verificar Token usa la misma clave secreta que authController
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
// 3. Inyectar datos del usuario verificado al request
|
||||
// authController firma: { id, dni, id_proveedor }
|
||||
req.user = decoded;
|
||||
|
||||
next();
|
||||
} catch (err) {
|
||||
console.warn(`[Auth] Token inválido desde IP: ${req.ip}`);
|
||||
return res.status(403).json({ success: false, error: 'INVALID_TOKEN', message: 'Token inválido o expirado' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = authenticateDevice;
|
||||
@@ -0,0 +1,26 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const verifyToken = (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
|
||||
// Check if header exists and format is "Bearer <token>"
|
||||
if (!authHeader) {
|
||||
return res.status(403).json({ error: "Access denied. No token provided." });
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1]; // Get token part
|
||||
|
||||
if (!token) {
|
||||
return res.status(403).json({ error: "Access denied. Malformed token." });
|
||||
}
|
||||
|
||||
try {
|
||||
const verified = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.user = verified; // Attach user info to request
|
||||
next(); // Proceed to route
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: "Invalid token" });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = verifyToken;
|
||||
@@ -0,0 +1,9 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const authController = require('../controllers/authController');
|
||||
|
||||
|
||||
router.post('/login', authController.login);
|
||||
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const locationController = require('../controllers/locationController');
|
||||
|
||||
const authenticateDevice = require('../middleware/auth');
|
||||
|
||||
// Route for background geolocation updates
|
||||
// The plugin might POST to /api/locations
|
||||
router.post('/api/locations', authenticateDevice, locationController.saveLocation);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const profileController = require('../controllers/profileController');
|
||||
const verifyToken = require('../middleware/authMiddleware');
|
||||
|
||||
// Modern routes (PROTECTED)
|
||||
router.post('/get_profile', verifyToken, profileController.getProfile);
|
||||
router.post('/change_password', verifyToken, profileController.changePassword);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,8 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const stressController = require('../controllers/stressController');
|
||||
|
||||
// GET /api/stress-test
|
||||
router.get('/stress-test', stressController.recordTest);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,56 @@
|
||||
const { initFirebaseAdmin } = require('../config/firebaseAdmin');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const getArgValue = (flag) => {
|
||||
const index = args.indexOf(flag);
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
return args[index + 1] || null;
|
||||
};
|
||||
|
||||
const token = getArgValue('--token') || process.env.FCM_TEST_TOKEN;
|
||||
const title = getArgValue('--title') || process.env.FCM_TEST_TITLE || 'Prueba desde Firebase Admin';
|
||||
const body = getArgValue('--body') || process.env.FCM_TEST_BODY || 'Notificación de prueba';
|
||||
const dryRun = args.includes('--dry-run');
|
||||
const selfTest = args.includes('--self-test');
|
||||
|
||||
const admin = initFirebaseAdmin();
|
||||
|
||||
if (selfTest) {
|
||||
console.log('Firebase Admin inicializado correctamente.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
console.error('Falta el token FCM. Usa --token o define FCM_TEST_TOKEN en el entorno.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const message = {
|
||||
token,
|
||||
notification: {
|
||||
title,
|
||||
body
|
||||
},
|
||||
data: {
|
||||
source: 'node-gestion-api',
|
||||
sentAt: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
|
||||
admin
|
||||
.messaging()
|
||||
.send(message, dryRun)
|
||||
.then((response) => {
|
||||
if (dryRun) {
|
||||
console.log('Dry run OK:', response);
|
||||
return;
|
||||
}
|
||||
console.log('Mensaje enviado:', response);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error al enviar la notificación:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user