diff --git a/node-api/src/routes/radius.js b/node-api/src/routes/radius.js index 232495d..3b8eb5d 100644 --- a/node-api/src/routes/radius.js +++ b/node-api/src/routes/radius.js @@ -3,12 +3,21 @@ import { VLAN_ID } from '../config/env.js'; import { buildAcceptPayload, normalizeAttributes } from '../utils/attrs.js'; import { pushRequest } from '../sse.js'; import { activeSessions, sendRadiusSelfTest } from '../services/radius.js'; +import { addDeviceToUser, connectDeviceForUser, disconnectDeviceForUser, getOrCreateDevice } from '../services/db.js'; const router = Router(); router.post('/authorize', (req, res) => { const attrs = normalizeAttributes(req.body); const reply = buildAcceptPayload(); + // Try to record device usage + const mac = attrs['Calling-Station-Id'] || attrs['Calling-Station-Id*0'] || ''; + const username = attrs['User-Name'] || attrs['User-Name*0'] || ''; + if (mac && username) { + getOrCreateDevice({ mac: String(mac) }).then(async (id) => { + await addDeviceToUser(String(username), id); + }).catch(() => {}); + } pushRequest({ id: Date.now() + ':' + Math.random().toString(16).slice(2), ts: new Date().toISOString(), @@ -37,8 +46,22 @@ router.post('/accounting', (req, res) => { calledStationId: attrs['Called-Station-Id'] || '', updatedAt: Date.now(), }); + // upsert device and link as connected + const mac = attrs['Calling-Station-Id'] || ''; + if (mac) { + getOrCreateDevice({ mac: String(mac) }).then(async (id) => { + await addDeviceToUser(String(username), id); + await connectDeviceForUser(String(username), id); + }).catch(() => {}); + } } else if (st === 'STOP') { activeSessions.delete(sessionId); + const mac = attrs['Calling-Station-Id'] || ''; + if (mac) { + getOrCreateDevice({ mac: String(mac) }).then(async (id) => { + await disconnectDeviceForUser(String(username), id); + }).catch(() => {}); + } } } } catch {} @@ -56,6 +79,13 @@ router.post('/authorize-inner', async (_req, res) => res.status(410).json({})); router.post('/post-auth', async (req, res) => { try { const attrs = normalizeAttributes(req.body); + const mac = attrs['Calling-Station-Id'] || attrs['Calling-Station-Id*0'] || ''; + const username = attrs['User-Name'] || attrs['User-Name*0'] || ''; + if (mac && username) { + getOrCreateDevice({ mac: String(mac) }).then(async (id) => { + await addDeviceToUser(String(username), id); + }).catch(() => {}); + } pushRequest({ id: Date.now() + ':' + Math.random().toString(16).slice(2), ts: new Date().toISOString(), @@ -91,4 +121,3 @@ router.post('/test/radius', async (_req, res) => { }); export default router; - diff --git a/node-api/src/services/db.js b/node-api/src/services/db.js index 87dd747..a9b131b 100644 --- a/node-api/src/services/db.js +++ b/node-api/src/services/db.js @@ -68,6 +68,70 @@ export async function ensureSchema() { } } +export async function getOrCreateDevice({ mac, nombre = null, vendor = null, descripcion = null }) { + if (!mac) throw new Error('mac required'); + const client = await pool.connect(); + try { + const up = await client.query( + `INSERT INTO dispositivos (mac, nombre, vendor, descripcion) + VALUES ($1, $2, $3, $4) + ON CONFLICT (mac) DO UPDATE SET + last_seen = NOW(), + nombre = COALESCE(dispositivos.nombre, EXCLUDED.nombre), + vendor = COALESCE(dispositivos.vendor, EXCLUDED.vendor), + descripcion = COALESCE(dispositivos.descripcion, EXCLUDED.descripcion) + RETURNING id`, + [String(mac), nombre, vendor, descripcion] + ); + return up.rows[0].id; + } finally { + client.release(); + } +} + +export async function ensureUserRow(username) { + await pool.query('INSERT INTO users (username) VALUES ($1) ON CONFLICT (username) DO NOTHING', [username]); +} + +export async function addDeviceToUser(username, deviceId) { + await ensureUserRow(username); + await pool.query( + `UPDATE users SET dispositivos_utilizados = ( + SELECT ARRAY( + SELECT DISTINCT x FROM ( + SELECT unnest(coalesce(dispositivos_utilizados, '{}'::int[])) AS x + UNION ALL SELECT $2::int + ) AS t + ) + ) WHERE username = $1`, + [username, deviceId] + ); +} + +export async function connectDeviceForUser(username, deviceId) { + await ensureUserRow(username); + await pool.query( + `UPDATE users SET dispositivos_conectados = ( + SELECT ARRAY( + SELECT DISTINCT x FROM ( + SELECT unnest(coalesce(dispositivos_conectados, '{}'::int[])) AS x + UNION ALL SELECT $2::int + ) AS t + ) + ) WHERE username = $1`, + [username, deviceId] + ); +} + +export async function disconnectDeviceForUser(username, deviceId) { + await ensureUserRow(username); + await pool.query( + `UPDATE users SET dispositivos_conectados = array_remove(coalesce(dispositivos_conectados, '{}'::int[]), $2::int) + WHERE username = $1`, + [username, deviceId] + ); +} + export async function readUsersFromDb() { const client = await pool.connect(); try {