cronjob de manejo de invitados listo
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { VLAN_ID } from '../config/env.js';
|
||||
import { clearRequests, getRecentRequests, registerSse } from '../sse.js';
|
||||
import { deleteUserFromDb, readUsersFromDb, upsertUserToDb, pool } from '../services/db.js';
|
||||
import { deleteUserFromDb, readUsersFromDb, upsertUserToDb, pool, disableGuestsFromYesterday } from '../services/db.js';
|
||||
import { disconnectUserSessions } from '../services/radius.js';
|
||||
|
||||
const router = Router();
|
||||
@@ -17,17 +17,31 @@ router.get('/users', async (_req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/users/:username', async (req, res) => {
|
||||
try {
|
||||
const uname = String(req.params.username);
|
||||
const items = await readUsersFromDb();
|
||||
const found = items.find(u => u.username === uname);
|
||||
if (!found) return res.status(404).json({ ok: false, error: 'not_found' });
|
||||
res.json({ ok: true, item: found });
|
||||
} catch (e) {
|
||||
console.error('GET /api/users/:username error:', e?.message || e);
|
||||
res.status(500).json({ ok: false, error: 'db_error' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/users', async (req, res) => {
|
||||
const { username, password, vlan, disabled } = req.body || {};
|
||||
const { username, password, vlan, disabled, etiquetas } = req.body || {};
|
||||
if (!username || !password) return res.status(400).json({ ok: false, error: 'username and password required' });
|
||||
const user = { username: String(username), password: String(password), vlan: String(vlan || VLAN_ID), disabled: !!disabled };
|
||||
if (Array.isArray(etiquetas)) user.etiquetas = etiquetas.map(String).slice(0, 100);
|
||||
await upsertUserToDb(user);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.patch('/users/:username', async (req, res) => {
|
||||
const uname = String(req.params.username);
|
||||
const { password, vlan, disabled } = req.body || {};
|
||||
const { password, vlan, disabled, etiquetas } = req.body || {};
|
||||
const current = (await readUsersFromDb()).find(u => u.username === uname);
|
||||
if (!current) return res.status(404).json({ ok: false, error: 'not_found' });
|
||||
const next = {
|
||||
@@ -36,6 +50,7 @@ router.patch('/users/:username', async (req, res) => {
|
||||
vlan: vlan !== undefined ? String(vlan) : current.vlan,
|
||||
disabled: disabled !== undefined ? !!disabled : current.disabled,
|
||||
};
|
||||
if (etiquetas !== undefined) next.etiquetas = Array.isArray(etiquetas) ? etiquetas.map(String).slice(0, 100) : current.etiquetas;
|
||||
await upsertUserToDb(next);
|
||||
if (disabled === true) {
|
||||
disconnectUserSessions(uname).catch(err => console.error('CoA disconnect error:', err));
|
||||
@@ -202,4 +217,15 @@ router.post('/devices/:id/disconnect', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Manual trigger: disable invited users from yesterday (America/Tegucigalpa day)
|
||||
router.post('/guests/disable-yesterday', async (_req, res) => {
|
||||
try {
|
||||
const { count } = await disableGuestsFromYesterday();
|
||||
res.json({ ok: true, count });
|
||||
} catch (e) {
|
||||
console.error('POST /api/guests/disable-yesterday error:', e?.message || e);
|
||||
res.status(500).json({ ok: false, error: 'db_error' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -176,7 +176,7 @@ export async function readUsersFromDb() {
|
||||
}
|
||||
|
||||
export async function upsertUserToDb(user) {
|
||||
const { username, password, vlan, disabled } = user;
|
||||
const { username, password, vlan, disabled, etiquetas } = user;
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
@@ -185,6 +185,9 @@ export async function upsertUserToDb(user) {
|
||||
'INSERT INTO users (username) VALUES ($1) ON CONFLICT (username) DO NOTHING',
|
||||
[username]
|
||||
);
|
||||
if (Array.isArray(etiquetas)) {
|
||||
await client.query('UPDATE users SET etiquetas = $2, updated_at = NOW() WHERE username = $1', [username, etiquetas.map(String).slice(0, 100)]);
|
||||
}
|
||||
await client.query("DELETE FROM radcheck WHERE username = $1 AND attribute = 'Cleartext-Password'", [username]);
|
||||
await client.query(
|
||||
"INSERT INTO radcheck (username, attribute, op, value) VALUES ($1,'Cleartext-Password',':=',$2)",
|
||||
@@ -234,6 +237,37 @@ export async function upsertUserToDb(user) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function disableGuestsFromYesterday() {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
const { rows } = await client.query(`
|
||||
WITH tz AS (
|
||||
SELECT (NOW() AT TIME ZONE 'America/Tegucigalpa')::date AS today
|
||||
)
|
||||
SELECT u.username
|
||||
FROM users u, tz
|
||||
WHERE 'invitado' = ANY(u.etiquetas)
|
||||
AND u.habilitado_since IS NOT NULL
|
||||
AND (u.habilitado_since AT TIME ZONE 'America/Tegucigalpa')::date = (tz.today - INTERVAL '1 day')::date
|
||||
`);
|
||||
const usernames = rows.map(r => r.username);
|
||||
if (usernames.length === 0) { await client.query('COMMIT'); return { count: 0 }; }
|
||||
await client.query("DELETE FROM radcheck WHERE attribute = 'Auth-Type' AND username = ANY($1)", [usernames]);
|
||||
const values = usernames.map(u => `('${u}','Auth-Type',':=','Reject')`).join(',');
|
||||
await client.query(`INSERT INTO radcheck (username, attribute, op, value) VALUES ${values}`);
|
||||
await client.query('UPDATE users SET updated_at = NOW() WHERE username = ANY($1)', [usernames]);
|
||||
await client.query('COMMIT');
|
||||
return { count: usernames.length };
|
||||
} catch (e) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error('disableGuestsFromYesterday error:', e?.message || e);
|
||||
throw e;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUserFromDb(username) {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user