// Reflector Dashboard — web front-end for SvxReflector // Copyright (C) 2026 SA7AUX (Björn) // SPDX-License-Identifier: AGPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify it under // the terms of the GNU Affero General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) any // later version. See the LICENSE file for details. 'use strict'; // Global Bans — callsign-only, styled like the DMR site's bans page. var $ = function(id) { return document.getElementById(id); }; var bansCanWrite = false; function bansEsc(s) { var d=document.createElement('div'); d.textContent=s==null?'':s; return d.innerHTML; } function bansSetMsg(txt, err) { var el = $('banMsg'); if (!el) return; if (!txt) { el.style.display='none'; return; } el.style.display='block'; el.textContent=txt; el.style.color=err?'#ff8080':'#00ff80'; } async function bapi(body) { var r = await fetch('/auth/admin_bans_api.php', { method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/json','X-CSRF-Token':window._csrfToken||''}, body:JSON.stringify(body) }); return r.json().catch(function(){ return null; }); } function bansRenderList(items) { var ul = $('banCsList'); if (!ul) return; ul.innerHTML = ''; if (!items || items.length === 0) { ul.innerHTML = '
  • — none —
  • '; return; } items.forEach(function(v) { var li = document.createElement('li'); li.className = 'd-flex justify-content-between align-items-center mb-1'; li.innerHTML = '' + bansEsc(v) + '' + (bansCanWrite ? '' : ''); ul.appendChild(li); }); } async function bansLoad() { var j = await bapi({action:'list'}); if (!j || !j.ok) return; bansCanWrite = !!j.can_write; var ro = $('banReadonlyBadge'); if (ro) ro.style.display = bansCanWrite ? 'none' : ''; document.querySelectorAll('.ban-add-row').forEach(function(el){ el.style.display = bansCanWrite ? 'flex' : 'none'; }); bansRenderList(j.callsigns || []); } async function bansAdd() { var v = ($('banCsAdd').value || '').trim(); if (!v) return; var res = await bapi({action:'add', kind:'callsign', value:v}); if (res && res.ok) { $('banCsAdd').value=''; bansSetMsg('Banned callsign: ' + v.toUpperCase(), false); await bansLoad(); } else bansSetMsg((res && res.error) || 'Failed', true); } async function bansRemove(v) { if (!confirm('Unban callsign ' + v + '?')) return; var res = await bapi({action:'remove', kind:'callsign', value:v}); if (res && res.ok) { bansSetMsg('Removed.', false); await bansLoad(); } else bansSetMsg((res && res.error) || 'Failed', true); } document.addEventListener('DOMContentLoaded', function() { fetch('/auth/me.php',{credentials:'same-origin'}).then(function(r){return r.json();}).then(function(j){if(j&&j.csrf_token)window._csrfToken=j.csrf_token;}).catch(function(){}); $('btnBansRefresh').addEventListener('click', bansLoad); document.querySelectorAll('button[data-ban-add]').forEach(function(b){ b.addEventListener('click', bansAdd); }); $('banCsAdd').addEventListener('keydown', function(e){ if (e.key==='Enter') bansAdd(); }); $('banCsList').addEventListener('click', function(e){ var btn = e.target.closest('button[data-ban-rm-val]'); if (btn) bansRemove(btn.getAttribute('data-ban-rm-val')); }); bansLoad(); });