import { error, info, success } from "../util/Console.js"; const queue = []; let running = false; function enqueue(task) { queue.push(task); if (!running) processQueue(); } async function processQueue() { if (queue.length === 0) { running = false; return; } running = true; const t = queue.shift(); try { await t(); } catch (err) { error('Q error ' + err); } setImmediate(processQueue); } async function opnsenseRequest(method, path, body = null) { const auth = Buffer.from(`${process.env.OPNSENSE_KEY}:${process.env.OPNSENSE_SECRET}`).toString('base64'); const opts = { method, headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/json', }, }; if (body) opts.body = JSON.stringify(body); const res = await fetch(`${process.env.OPNSENSE_HOST}${path}`, opts); const text = await res.text(); try { return JSON.parse(text); } catch { error(`got non-json response: ${text}`); return null; } } async function applyRules() { await opnsenseRequest('POST', '/api/firewall/d_nat/apply'); } export function enqueueNatAdd(ip, port, proto = 'tcp/udp') { enqueue(async () => { info(`adding rule udp+tcp at ${ip}:${port}`); await opnsenseRequest('POST', '/api/firewall/d_nat/addRule', { rule: { interface: 'wan', protocol: 'tcp/udp', destination: { network: 'wanip', port: `${port}`, }, target: ip, 'local-port': `${port}`, descr: `pelican-${ip}-${port}`, disabled: '0', } }); await applyRules(); success(`added rule udp+tcp at ${ip}:${port}`); }); } export function enqueueNatRemove(ip, port, proto = 'tcp/udp') { enqueue(async () => { info(`deleting rule udp+tcp at ${ip}:${port}`); const result = await opnsenseRequest('POST', '/api/firewall/d_nat/searchRule', { current: 1, rowCount: 1000, searchPhrase: `pelican-${ip}-${port}`, }) const rows = result?.rows || []; for (const rule of rows) { if (rule.descr === `pelican-${ip}-${port}`) { await opnsenseRequest('POST', `/api/firewall/d_nat/delRule/${rule.uuid}`); } } await applyRules(); success(`deleted rule udp+tcp at ${ip}:${port}`); }); }