mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-05-19 14:16:47 -04:00
Compare commits
43 Commits
v26.5.4
...
next_relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eaaf50caf | ||
|
|
f0684c66c2 | ||
|
|
328e591fcd | ||
|
|
80c8a66396 | ||
|
|
0517da2405 | ||
|
|
35dc9f9fa0 | ||
|
|
af14dea40c | ||
|
|
292223e062 | ||
|
|
47a559197f | ||
|
|
bf4e0b4a7c | ||
|
|
92adaddabb | ||
|
|
7725697b87 | ||
|
|
09744f3acd | ||
|
|
f6b34845e0 | ||
|
|
198ca5d410 | ||
|
|
4761a688fc | ||
|
|
6e149f8ad3 | ||
|
|
4cac81b5ec | ||
|
|
03938d8c28 | ||
|
|
0127194816 | ||
|
|
13f8858319 | ||
|
|
f11a63bb7f | ||
|
|
556d104bbb | ||
|
|
eafbcd52f8 | ||
|
|
2fac875792 | ||
|
|
6d661dd12c | ||
|
|
39068e9824 | ||
|
|
957c779cb5 | ||
|
|
76612e5d0e | ||
|
|
7273899e3e | ||
|
|
608686e4bd | ||
|
|
b290d3c3d2 | ||
|
|
781ae2f91c | ||
|
|
f428f45ad2 | ||
|
|
b7ebc8206f | ||
|
|
9575692a39 | ||
|
|
1def218db5 | ||
|
|
300820e6bd | ||
|
|
8f7f7eaed7 | ||
|
|
190262a730 | ||
|
|
5ba202c6a1 | ||
|
|
e262f915e2 | ||
|
|
b8c4e62ad5 |
@@ -157,7 +157,7 @@ Check the [GitHub Issues](https://github.com/netalertx/NetAlertX/issues) for the
|
|||||||
## Everything else
|
## Everything else
|
||||||
<!--- --------------------------------------------------------------------- --->
|
<!--- --------------------------------------------------------------------- --->
|
||||||
|
|
||||||
<a href="https://trendshift.io/repositories/12670" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12670" alt="jokob-sk%2FNetAlertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/19712" target="_blank"><img src="https://trendshift.io/api/badge/repositories/19712" alt="jokob-sk%2FNetAlertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
### 📧 Get notified what's new
|
### 📧 Get notified what's new
|
||||||
|
|
||||||
@@ -170,6 +170,7 @@ Get notified about a new release, what new functionality you can use and about b
|
|||||||
- [Fing](https://www.fing.com/) - Network scanner app for your Internet security (Commercial, Phone App, Proprietary hardware)
|
- [Fing](https://www.fing.com/) - Network scanner app for your Internet security (Commercial, Phone App, Proprietary hardware)
|
||||||
- [NetBox](https://netboxlabs.com/) - The gold standard for Network Source of Truth (NSoT) and IPAM.
|
- [NetBox](https://netboxlabs.com/) - The gold standard for Network Source of Truth (NSoT) and IPAM.
|
||||||
- [Zabbix](https://www.zabbix.com/) or [Nagios](https://www.nagios.org/) - Strong focus on infrastructure monitoring.
|
- [Zabbix](https://www.zabbix.com/) or [Nagios](https://www.nagios.org/) - Strong focus on infrastructure monitoring.
|
||||||
|
- [Domotz](https://www.domotz.com/) - Commercial network monitoring and remote management platform aimed at MSPs, IT teams, and multi-site environments.
|
||||||
- [NetAlertX](https://netalertx.com) - The streamlined, discovery-focused choice for real-time asset intelligence and noise-free alerting.
|
- [NetAlertX](https://netalertx.com) - The streamlined, discovery-focused choice for real-time asset intelligence and noise-free alerting.
|
||||||
|
|
||||||
### 💙 Donations
|
### 💙 Donations
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ REPORT_DASHBOARD_URL='update_REPORT_DASHBOARD_URL_setting'
|
|||||||
INTRNT_RUN='schedule'
|
INTRNT_RUN='schedule'
|
||||||
ARPSCAN_RUN='schedule'
|
ARPSCAN_RUN='schedule'
|
||||||
NSLOOKUP_RUN='before_name_updates'
|
NSLOOKUP_RUN='before_name_updates'
|
||||||
|
DIGSCAN_RUN='before_name_updates'
|
||||||
AVAHISCAN_RUN='before_name_updates'
|
AVAHISCAN_RUN='before_name_updates'
|
||||||
NBTSCAN_RUN='before_name_updates'
|
NBTSCAN_RUN='before_name_updates'
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ docker run -d --rm --network=host \
|
|||||||
|
|
||||||
> Runtime UID/GID: The image defaults to a service user `netalertx` (UID/GID 20211). A separate readonly lock owner also uses UID/GID 20211 for 004/005 immutability. You can override the runtime UID/GID at build (ARG) or run (`--user` / compose `user:`) but must align writable mounts (`/data`, `/tmp*`) and tmpfs `uid/gid` to that choice.
|
> Runtime UID/GID: The image defaults to a service user `netalertx` (UID/GID 20211). A separate readonly lock owner also uses UID/GID 20211 for 004/005 immutability. You can override the runtime UID/GID at build (ARG) or run (`--user` / compose `user:`) but must align writable mounts (`/data`, `/tmp*`) and tmpfs `uid/gid` to that choice.
|
||||||
|
|
||||||
See alternative [docked-compose examples](https://docs.netalertx.com/DOCKER_COMPOSE).
|
See alternative [docker-compose examples](https://docs.netalertx.com/DOCKER_COMPOSE).
|
||||||
|
|
||||||
### Default ports
|
### Default ports
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
|
|||||||
| `INTRNT` | [internet_ip](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_ip/) | 🔍 | Internet IP scanner | | |
|
| `INTRNT` | [internet_ip](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_ip/) | 🔍 | Internet IP scanner | | |
|
||||||
| `INTRSPD` | [internet_speedtest](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_speedtest/) | ♻ | Internet speed test | | |
|
| `INTRSPD` | [internet_speedtest](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/internet_speedtest/) | ♻ | Internet speed test | | |
|
||||||
| `IPNEIGH` | [ipneigh](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/ipneigh/) | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | |
|
| `IPNEIGH` | [ipneigh](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/ipneigh/) | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | |
|
||||||
|
| `KEALSS` | [kea_api](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/kea_api/) | 🔍/🆎 | Pull lease data from the Kea DHCP API | | |
|
||||||
| `LUCIRPC` | [luci_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/luci_import/) | 🔍 | Import connected devices from OpenWRT | | |
|
| `LUCIRPC` | [luci_import](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/luci_import/) | 🔍 | Import connected devices from OpenWRT | | |
|
||||||
| `MAINT` | [maintenance](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/maintenance/) | ⚙ | Maintenance of logs, etc. | | |
|
| `MAINT` | [maintenance](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/maintenance/) | ⚙ | Maintenance of logs, etc. | | |
|
||||||
| `MQTT` | [_publisher_mqtt](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | |
|
| `MQTT` | [_publisher_mqtt](https://github.com/netalertx/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | |
|
||||||
|
|||||||
@@ -307,7 +307,10 @@ function updateChevrons(currentMac) {
|
|||||||
pos = refreshedList.findIndex(item => item.devMac === currentMac);
|
pos = refreshedList.findIndex(item => item.devMac === currentMac);
|
||||||
|
|
||||||
if (pos === -1) {
|
if (pos === -1) {
|
||||||
console.error('Still not found after re-cache:', currentMac);
|
console.warn('Device not found in device list after re-cache — hiding navigation controls:', currentMac);
|
||||||
|
$('#txtRecord').hide();
|
||||||
|
$('#btnPrevious').hide();
|
||||||
|
$('#btnNext').hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,26 +502,9 @@ async function renderSmallBoxes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDevicePageName(mac) {
|
// ----------------------------------------
|
||||||
let name = getDevDataByMac(mac, "devName");
|
// Write device name/owner into page title DOM. Pure DOM side-effect, no data fetching.
|
||||||
let owner = getDevDataByMac(mac, "devOwner");
|
function applyDevicePageTitle(mac, name, owner) {
|
||||||
|
|
||||||
// If data is missing, re-cache and retry once
|
|
||||||
if (mac != 'new' && (name === null|| owner === null)) {
|
|
||||||
console.warn("Device not found in cache, retrying after re-cache:", mac);
|
|
||||||
showSpinner();
|
|
||||||
cacheDevices(true).then(() => {
|
|
||||||
hideSpinner();
|
|
||||||
// Retry after successful cache
|
|
||||||
updateDevicePageName(mac);
|
|
||||||
}).catch((err) => {
|
|
||||||
hideSpinner();
|
|
||||||
console.error("Failed to refresh devices:", err);
|
|
||||||
});
|
|
||||||
return; // Exit early to avoid showing bad data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page title - Name
|
|
||||||
let pageTitleText;
|
let pageTitleText;
|
||||||
|
|
||||||
if (mac === "new") {
|
if (mac === "new") {
|
||||||
@@ -530,12 +516,12 @@ function updateDevicePageName(mac) {
|
|||||||
`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info")
|
`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info")
|
||||||
);
|
);
|
||||||
$('#devicePageInfoPlc').show();
|
$('#devicePageInfoPlc').show();
|
||||||
} else if (!owner || name.toString().includes(owner)) {
|
} else if (!owner || (name && name.toString().includes(owner))) {
|
||||||
pageTitleText = name;
|
pageTitleText = name ?? getString("DevDetail_EveandAl_NewDevice");
|
||||||
$('#pageTitle').html(pageTitleText);
|
$('#pageTitle').html(pageTitleText);
|
||||||
$('#devicePageInfoPlc').hide();
|
$('#devicePageInfoPlc').hide();
|
||||||
} else {
|
} else {
|
||||||
pageTitleText = `${name} (${owner})`;
|
pageTitleText = `${name ?? getString("DevDetail_EveandAl_NewDevice")} (${owner})`;
|
||||||
$('#pageTitle').html(pageTitleText);
|
$('#pageTitle').html(pageTitleText);
|
||||||
$('#devicePageInfoPlc').hide();
|
$('#devicePageInfoPlc').hide();
|
||||||
}
|
}
|
||||||
@@ -544,6 +530,53 @@ function updateDevicePageName(mac) {
|
|||||||
$('title').html(pageTitleText + ' - ' + $('title').html());
|
$('title').html(pageTitleText + ' - ' + $('title').html());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Resolve device name/owner for the page title.
|
||||||
|
// Stage 1: localStorage cache (synchronous, fast path).
|
||||||
|
// Stage 2: one forced re-cache from table_devices.json.
|
||||||
|
// Stage 3: REST API fallback so a direct-link visit never loops.
|
||||||
|
async function updateDevicePageName(mac) {
|
||||||
|
let name = getDevDataByMac(mac, "devName");
|
||||||
|
let owner = getDevDataByMac(mac, "devOwner");
|
||||||
|
|
||||||
|
// Stage 2: one re-cache attempt
|
||||||
|
if (mac !== 'new' && name === null) {
|
||||||
|
console.warn("Device not in cache, attempting re-cache:", mac);
|
||||||
|
showSpinner();
|
||||||
|
try {
|
||||||
|
await cacheDevices(true);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Re-cache failed:", err);
|
||||||
|
} finally {
|
||||||
|
hideSpinner();
|
||||||
|
}
|
||||||
|
name = getDevDataByMac(mac, "devName");
|
||||||
|
owner = getDevDataByMac(mac, "devOwner");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 3: REST fallback — same endpoint renderSmallBoxes uses, always DB-direct
|
||||||
|
if (mac !== 'new' && name === null) {
|
||||||
|
console.warn("Device not found in cache after re-cache, falling back to REST API:", mac);
|
||||||
|
try {
|
||||||
|
const { apiBase, authHeader } = getAuthContext();
|
||||||
|
const res = await fetch(`${apiBase}/device/${encodeURIComponent(mac)}`, {
|
||||||
|
headers: authHeader
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
name = data.devName ?? null;
|
||||||
|
owner = data.devOwner ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("REST fallback for device name returned:", res.status);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("REST fallback error:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyDevicePageTitle(mac, name, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,6 @@ function initializeSessionsDatatable (sessionsRows) {
|
|||||||
|
|
||||||
if (!cellData.includes("missing event") && !cellData.includes("..."))
|
if (!cellData.includes("missing event") && !cellData.includes("..."))
|
||||||
{
|
{
|
||||||
if (cellData.includes("+")) { // Check if timezone offset is present
|
|
||||||
cellData = cellData.split('+')[0]; // Remove timezone offset
|
|
||||||
}
|
|
||||||
// console.log(cellData);
|
// console.log(cellData);
|
||||||
result = localizeTimestamp(cellData);
|
result = localizeTimestamp(cellData);
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -10832,7 +10832,8 @@ if (typeof jQuery === 'undefined') {
|
|||||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||||
}
|
}
|
||||||
|
|
||||||
var $parent = $(selector === '#' ? [] : selector)
|
selector = selector === '#' ? [] : selector
|
||||||
|
var $parent = $(document).find(selector)
|
||||||
|
|
||||||
if (e) e.preventDefault()
|
if (e) e.preventDefault()
|
||||||
|
|
||||||
@@ -11228,9 +11229,15 @@ if (typeof jQuery === 'undefined') {
|
|||||||
// =================
|
// =================
|
||||||
|
|
||||||
var clickHandler = function (e) {
|
var clickHandler = function (e) {
|
||||||
var href
|
|
||||||
var $this = $(this)
|
var $this = $(this)
|
||||||
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
|
var href = $this.attr('href')
|
||||||
|
if (href) {
|
||||||
|
href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|
||||||
|
}
|
||||||
|
|
||||||
|
var target = $this.attr('data-target') || href
|
||||||
|
var $target = $(document).find(target)
|
||||||
|
|
||||||
if (!$target.hasClass('carousel')) return
|
if (!$target.hasClass('carousel')) return
|
||||||
var options = $.extend({}, $target.data(), $this.data())
|
var options = $.extend({}, $target.data(), $this.data())
|
||||||
var slideIndex = $this.attr('data-slide-to')
|
var slideIndex = $this.attr('data-slide-to')
|
||||||
@@ -11420,7 +11427,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
var target = $trigger.attr('data-target')
|
var target = $trigger.attr('data-target')
|
||||||
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|
||||||
|
|
||||||
return $(target)
|
return $(document).find(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -11502,7 +11509,7 @@ if (typeof jQuery === 'undefined') {
|
|||||||
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||||
}
|
}
|
||||||
|
|
||||||
var $parent = selector && $(selector)
|
var $parent = selector && $(document).find(selector)
|
||||||
|
|
||||||
return $parent && $parent.length ? $parent : $this.parent()
|
return $parent && $parent.length ? $parent : $this.parent()
|
||||||
}
|
}
|
||||||
@@ -11961,7 +11968,10 @@ if (typeof jQuery === 'undefined') {
|
|||||||
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
|
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
|
||||||
var $this = $(this)
|
var $this = $(this)
|
||||||
var href = $this.attr('href')
|
var href = $this.attr('href')
|
||||||
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
|
var target = $this.attr('data-target') ||
|
||||||
|
(href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
|
||||||
|
|
||||||
|
var $target = $(document).find(target)
|
||||||
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
|
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
|
||||||
|
|
||||||
if ($this.is('a')) e.preventDefault()
|
if ($this.is('a')) e.preventDefault()
|
||||||
|
|||||||
@@ -1842,11 +1842,16 @@
|
|||||||
return globalLocale;
|
return globalLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLocaleNameSane(name) {
|
||||||
|
// Prevent names that look like filesystem paths, i.e contain '/' or '\'
|
||||||
|
return name.match('^[^/\\\\]*$') != null;
|
||||||
|
}
|
||||||
|
|
||||||
function loadLocale(name) {
|
function loadLocale(name) {
|
||||||
var oldLocale = null;
|
var oldLocale = null;
|
||||||
// TODO: Find a better way to register and load all the locales in Node
|
// TODO: Find a better way to register and load all the locales in Node
|
||||||
if (!locales[name] && (typeof module !== 'undefined') &&
|
if (!locales[name] && (typeof module !== 'undefined') &&
|
||||||
module && module.exports) {
|
module && module.exports && isLocaleNameSane(name)) {
|
||||||
try {
|
try {
|
||||||
oldLocale = globalLocale._abbr;
|
oldLocale = globalLocale._abbr;
|
||||||
var aliasedRequire = require;
|
var aliasedRequire = require;
|
||||||
@@ -2294,7 +2299,7 @@
|
|||||||
|
|
||||||
function preprocessRFC2822(s) {
|
function preprocessRFC2822(s) {
|
||||||
// Remove comments and folding whitespace and replace multiple-spaces with a single space
|
// Remove comments and folding whitespace and replace multiple-spaces with a single space
|
||||||
return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
return s.replace(/\((?:(?!\().)*\)|[\n\t]/gs, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkWeekday(weekdayStr, parsedInput, config) {
|
function checkWeekday(weekdayStr, parsedInput, config) {
|
||||||
|
|||||||
@@ -66,8 +66,8 @@
|
|||||||
"CustProps_cant_remove": "No es pot eliminar, es necessita una propietat mínim.",
|
"CustProps_cant_remove": "No es pot eliminar, es necessita una propietat mínim.",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Això és una configuració de manteniment. Especifica el nombre de dies que es conservaran els esdeveniments. Els esdeveniments antics s'esborraran periòdicament. També aplica als esdeveniments dels Connectors (Plugins).",
|
"DAYS_TO_KEEP_EVENTS_description": "Això és una configuració de manteniment. Especifica el nombre de dies que es conservaran els esdeveniments. Els esdeveniments antics s'esborraran periòdicament. També aplica als esdeveniments dels Connectors (Plugins).",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Esborrar esdeveniments més vells de",
|
"DAYS_TO_KEEP_EVENTS_name": "Esborrar esdeveniments més vells de",
|
||||||
"DEEP_SLEEP_description": "",
|
"DEEP_SLEEP_description": "Redueix l'ús de la CPU ampliant els temps d'espera inactiu entre els cicles de processament. Quan està activat, les exploracions es poden retardar fins a 1 minut i la interfície d'usuari pot ser menys sensible.",
|
||||||
"DEEP_SLEEP_name": "",
|
"DEEP_SLEEP_name": "Son profund",
|
||||||
"DISCOVER_PLUGINS_description": "Desactiva aquesta opció per accelerar la inicialització i l'estalvi de configuració. Quan està desactivat, els connectors no es descobreixen, i no podeu afegir nous connectors a la configuració <code>LOADED_PLUGINS</code>.",
|
"DISCOVER_PLUGINS_description": "Desactiva aquesta opció per accelerar la inicialització i l'estalvi de configuració. Quan està desactivat, els connectors no es descobreixen, i no podeu afegir nous connectors a la configuració <code>LOADED_PLUGINS</code>.",
|
||||||
"DISCOVER_PLUGINS_name": "Descobreix els plugins",
|
"DISCOVER_PLUGINS_name": "Descobreix els plugins",
|
||||||
"DevDetail_Children_Title": "Relacions filles",
|
"DevDetail_Children_Title": "Relacions filles",
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
"DevDetail_SessionTable_Duration": "Durada",
|
"DevDetail_SessionTable_Duration": "Durada",
|
||||||
"DevDetail_SessionTable_IP": "IP",
|
"DevDetail_SessionTable_IP": "IP",
|
||||||
"DevDetail_SessionTable_Order": "Ordre",
|
"DevDetail_SessionTable_Order": "Ordre",
|
||||||
"DevDetail_Shortcut_CurrentStatus": "Estat actual",
|
"DevDetail_Shortcut_CurrentStatus": "Estat",
|
||||||
"DevDetail_Shortcut_DownAlerts": "Aturar alertes",
|
"DevDetail_Shortcut_DownAlerts": "Aturar alertes",
|
||||||
"DevDetail_Shortcut_Presence": "Presència",
|
"DevDetail_Shortcut_Presence": "Presència",
|
||||||
"DevDetail_Shortcut_Sessions": "Sessions",
|
"DevDetail_Shortcut_Sessions": "Sessions",
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
"Device_TableHead_NetworkSite": "Network Site",
|
"Device_TableHead_NetworkSite": "Network Site",
|
||||||
"Device_TableHead_Owner": "Propietari",
|
"Device_TableHead_Owner": "Propietari",
|
||||||
"Device_TableHead_ParentRelType": "Tipus de relació",
|
"Device_TableHead_ParentRelType": "Tipus de relació",
|
||||||
"Device_TableHead_Parent_MAC": "Node pare de xarxa",
|
"Device_TableHead_Parent_MAC": "Node pare",
|
||||||
"Device_TableHead_Port": "Port",
|
"Device_TableHead_Port": "Port",
|
||||||
"Device_TableHead_PresentLastScan": "Presència",
|
"Device_TableHead_PresentLastScan": "Presència",
|
||||||
"Device_TableHead_ReqNicsOnline": "Requereix NICs En línia",
|
"Device_TableHead_ReqNicsOnline": "Requereix NICs En línia",
|
||||||
@@ -346,7 +346,7 @@
|
|||||||
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
|
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
|
||||||
"Gen_NetworkMask": "Màscara de xarxa",
|
"Gen_NetworkMask": "Màscara de xarxa",
|
||||||
"Gen_New": "Nou",
|
"Gen_New": "Nou",
|
||||||
"Gen_No_Data": "",
|
"Gen_No_Data": "Sense dades",
|
||||||
"Gen_Offline": "Fora de línia",
|
"Gen_Offline": "Fora de línia",
|
||||||
"Gen_Okay": "Ok",
|
"Gen_Okay": "Ok",
|
||||||
"Gen_Online": "En línia",
|
"Gen_Online": "En línia",
|
||||||
@@ -808,4 +808,4 @@
|
|||||||
"settings_system_label": "Sistema",
|
"settings_system_label": "Sistema",
|
||||||
"settings_update_item_warning": "Actualitza el valor sota. Sigues curós de seguir el format anterior. <b>No hi ha validació.</b>",
|
"settings_update_item_warning": "Actualitza el valor sota. Sigues curós de seguir el format anterior. <b>No hi ha validació.</b>",
|
||||||
"test_event_tooltip": "Deseu els canvis primer abans de comprovar la configuració."
|
"test_event_tooltip": "Deseu els canvis primer abans de comprovar la configuració."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,13 +382,13 @@
|
|||||||
"HRS_TO_KEEP_NEWDEV_name": "Odstranit nová zařízení po",
|
"HRS_TO_KEEP_NEWDEV_name": "Odstranit nová zařízení po",
|
||||||
"HRS_TO_KEEP_OFFDEV_description": "Toto je údržbářské nastavení <b>ODSTRANĚNÍ zařízení</b>. Pokud je povoleno (<code>0</code> zakázáno), zařízení <b>Offline</b> s jejich datumem <b>Posledního připojení</b> starším, než uvedené hodiny v tomto nastavení, budou odstraněna. Použijte toto nastavení, pokud chcete automaticky mazat <b>Offline zařízení</b> po uplynutí <code>X</code> hodin offline.",
|
"HRS_TO_KEEP_OFFDEV_description": "Toto je údržbářské nastavení <b>ODSTRANĚNÍ zařízení</b>. Pokud je povoleno (<code>0</code> zakázáno), zařízení <b>Offline</b> s jejich datumem <b>Posledního připojení</b> starším, než uvedené hodiny v tomto nastavení, budou odstraněna. Použijte toto nastavení, pokud chcete automaticky mazat <b>Offline zařízení</b> po uplynutí <code>X</code> hodin offline.",
|
||||||
"HRS_TO_KEEP_OFFDEV_name": "Odstranit offline zařízení po",
|
"HRS_TO_KEEP_OFFDEV_name": "Odstranit offline zařízení po",
|
||||||
"LOADED_PLUGINS_description": "",
|
"LOADED_PLUGINS_description": "Které pluginy načíst. Přidávání pluginů může aplikaci zpomalit. Přečtěte si více o pluginech, které musí být povoleny, o typech, nebo o scannovacích moźnostech v <a target=\"_blank\" href=\"https://docs.netalertx.com/PLUGINS\">dokumentaci pluginů</a>. Odpojené pluginy ztratí vaše nastavení. Pouze <code>deaktivované</code> pluginy mohou být odpojeny.",
|
||||||
"LOADED_PLUGINS_name": "Načtené pluginy",
|
"LOADED_PLUGINS_name": "Načtené pluginy",
|
||||||
"LOG_LEVEL_description": "Toto nastavení způsobí více detailní logování. To je užitečné pro ladění událostí zapisujících do databáze.",
|
"LOG_LEVEL_description": "Toto nastavení způsobí více detailní logování. To je užitečné pro ladění událostí zapisujících do databáze.",
|
||||||
"LOG_LEVEL_name": "Vypisovat dodatečné logování",
|
"LOG_LEVEL_name": "Vypisovat dodatečné logování",
|
||||||
"Loading": "Načítání…",
|
"Loading": "Načítání…",
|
||||||
"Login_Box": "Zadejte vaše heslo",
|
"Login_Box": "Zadejte vaše heslo",
|
||||||
"Login_Default_PWD": "",
|
"Login_Default_PWD": "Výchozí heslo \"123456\" je stále aktivní.",
|
||||||
"Login_Info": "Hesla jsou nastavována přes Set Password plugin. Zkontrolujte <a target=\"_blank\" href=\"https://github.com/netalertx/NetAlertX/tree/main/front/plugins/set_password\">dokumentaci SETPWD</a>, pokud máte potíže s přihlášením.",
|
"Login_Info": "Hesla jsou nastavována přes Set Password plugin. Zkontrolujte <a target=\"_blank\" href=\"https://github.com/netalertx/NetAlertX/tree/main/front/plugins/set_password\">dokumentaci SETPWD</a>, pokud máte potíže s přihlášením.",
|
||||||
"Login_Psw-box": "Heslo",
|
"Login_Psw-box": "Heslo",
|
||||||
"Login_Psw_alert": "Upozornění na heslo!",
|
"Login_Psw_alert": "Upozornění na heslo!",
|
||||||
@@ -437,32 +437,32 @@
|
|||||||
"Maintenance_Tool_UnlockFields_text": "Tento nástroj odstraní všechny zdrojové hodnoty ze všech sledovaných polí pro všechna zařízení, čímž efektivně odemkne všechna pole pro pluginy a uživatele. Používejte jej opatrně, protože to ovlivní celý inventář vašich zařízení.",
|
"Maintenance_Tool_UnlockFields_text": "Tento nástroj odstraní všechny zdrojové hodnoty ze všech sledovaných polí pro všechna zařízení, čímž efektivně odemkne všechna pole pro pluginy a uživatele. Používejte jej opatrně, protože to ovlivní celý inventář vašich zařízení.",
|
||||||
"Maintenance_Tool_arpscansw": "Přepnout ARP sken (zapnuto/vypnuto)",
|
"Maintenance_Tool_arpscansw": "Přepnout ARP sken (zapnuto/vypnuto)",
|
||||||
"Maintenance_Tool_arpscansw_noti": "Přepne ARP sken na zapnuto nebo vypnuto",
|
"Maintenance_Tool_arpscansw_noti": "Přepne ARP sken na zapnuto nebo vypnuto",
|
||||||
"Maintenance_Tool_arpscansw_noti_text": "Pokud je scan vypnut, zůstává vypnutý do opětovné aktivace.",
|
"Maintenance_Tool_arpscansw_noti_text": "Pokud je sken vypnut, zůstává vypnutý do opětovné aktivace.",
|
||||||
"Maintenance_Tool_arpscansw_text": "",
|
"Maintenance_Tool_arpscansw_text": "Přepíná ARP-SCAN na zapnuto, nebo vypnuto. Jakmile je skenování vypnuto, zůstává vypnuto, dokud není znovu aktivováno. Aktivní skenování nejsou přerušena.",
|
||||||
"Maintenance_Tool_backup": "",
|
"Maintenance_Tool_backup": "Záloha DB",
|
||||||
"Maintenance_Tool_backup_noti": "",
|
"Maintenance_Tool_backup_noti": "Záloha DB",
|
||||||
"Maintenance_Tool_backup_noti_text": "",
|
"Maintenance_Tool_backup_noti_text": "Opravdu chcete spustit zálohu DB? Ujistěte se, že neběží žádný sken.",
|
||||||
"Maintenance_Tool_backup_text": "",
|
"Maintenance_Tool_backup_text": "Databázové zálohy jsou umístěny v databázovém adresáři jako ZIP archiv pojmenovaný podle jeho data vytvoření. Počet backupů není limitován.",
|
||||||
"Maintenance_Tool_check_visible": "",
|
"Maintenance_Tool_check_visible": "Odšrktněte pro skrytí sloupce.",
|
||||||
"Maintenance_Tool_clearSourceFields_selected": "",
|
"Maintenance_Tool_clearSourceFields_selected": "Vymazat zrojové položky",
|
||||||
"Maintenance_Tool_clearSourceFields_selected_noti": "",
|
"Maintenance_Tool_clearSourceFields_selected_noti": "Vymazat zdroje",
|
||||||
"Maintenance_Tool_clearSourceFields_selected_text": "",
|
"Maintenance_Tool_clearSourceFields_selected_text": "Toto vymaže všechny zdrojové položky zvolených zařízení. Tuto akci nelze vrátit zpět.",
|
||||||
"Maintenance_Tool_darkmode": "",
|
"Maintenance_Tool_darkmode": "Přepnutí režimů (Tmavý/Světlý)",
|
||||||
"Maintenance_Tool_darkmode_noti": "",
|
"Maintenance_Tool_darkmode_noti": "Přepnutí režimů",
|
||||||
"Maintenance_Tool_darkmode_noti_text": "",
|
"Maintenance_Tool_darkmode_noti_text": "Po změně tématu se stránka pokusí znovu načíst pro aktivaci změn. Pokud bude potřeba, musí být vymazána cache.",
|
||||||
"Maintenance_Tool_darkmode_text": "",
|
"Maintenance_Tool_darkmode_text": "Přepíná mezi tmavým a světlým režimem. Pokud přepínač nefunguje správně, zkuste vymazat cache prohlížeče. Změny jsou provedeny na straně serveru, takže ovlivňují všechna použitá zařízení.",
|
||||||
"Maintenance_Tool_del_ActHistory": "",
|
"Maintenance_Tool_del_ActHistory": "Odstranění síťové aktivity",
|
||||||
"Maintenance_Tool_del_ActHistory_noti": "",
|
"Maintenance_Tool_del_ActHistory_noti": "Odstranit síťovou aktivitu",
|
||||||
"Maintenance_Tool_del_ActHistory_noti_text": "",
|
"Maintenance_Tool_del_ActHistory_noti_text": "Opravdu chcete resetovat síťovou aktivitu?",
|
||||||
"Maintenance_Tool_del_ActHistory_text": "",
|
"Maintenance_Tool_del_ActHistory_text": "Grafy síťové aktivity byly resetovány. Toto neovlivní události.",
|
||||||
"Maintenance_Tool_del_alldev": "",
|
"Maintenance_Tool_del_alldev": "Odstanit všechna zařízení",
|
||||||
"Maintenance_Tool_del_alldev_noti": "",
|
"Maintenance_Tool_del_alldev_noti": "Odstranit zařízení",
|
||||||
"Maintenance_Tool_del_alldev_noti_text": "",
|
"Maintenance_Tool_del_alldev_noti_text": "Opravdu chcete odstranit všechna zařízení?",
|
||||||
"Maintenance_Tool_del_alldev_text": "",
|
"Maintenance_Tool_del_alldev_text": "Před použitím této funkce prosím proveďte zálohu. Odstranění nelze vrátit zpět. Všechna zařízení budou z databáze odstraněna.",
|
||||||
"Maintenance_Tool_del_allevents": "",
|
"Maintenance_Tool_del_allevents": "Odstranit události (reset přítomnosti)",
|
||||||
"Maintenance_Tool_del_allevents30": "",
|
"Maintenance_Tool_del_allevents30": "Odstranit všechny události staří 30 dní",
|
||||||
"Maintenance_Tool_del_allevents30_noti": "",
|
"Maintenance_Tool_del_allevents30_noti": "Odstranit události",
|
||||||
"Maintenance_Tool_del_allevents30_noti_text": "",
|
"Maintenance_Tool_del_allevents30_noti_text": "Opravdu chcete odstranit všechny události starší 30 dní? Toto vyresetuje přítomnost všech zařízení.",
|
||||||
"Maintenance_Tool_del_allevents30_text": "",
|
"Maintenance_Tool_del_allevents30_text": "",
|
||||||
"Maintenance_Tool_del_allevents_noti": "",
|
"Maintenance_Tool_del_allevents_noti": "",
|
||||||
"Maintenance_Tool_del_allevents_noti_text": "",
|
"Maintenance_Tool_del_allevents_noti_text": "",
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"BackDevDetail_Tools_WOL_error": "O comando NÃO foi executado.",
|
"BackDevDetail_Tools_WOL_error": "O comando NÃO foi executado.",
|
||||||
"BackDevDetail_Tools_WOL_okay": "O comando foi executado.",
|
"BackDevDetail_Tools_WOL_okay": "O comando foi executado.",
|
||||||
"BackDevices_Arpscan_disabled": "Análise Arp Desativada",
|
"BackDevices_Arpscan_disabled": "Análise Arp Desativada",
|
||||||
"BackDevices_Arpscan_enabled": "Análise ARP Ativada",
|
"BackDevices_Arpscan_enabled": "Análise Arp Ativada",
|
||||||
"BackDevices_Backup_CopError": "A base da dados original não pode ser gravada.",
|
"BackDevices_Backup_CopError": "A base da dados original não pode ser gravada.",
|
||||||
"BackDevices_Backup_Failed": "A copia de segurança foi parcialmente executada. O arquivo não pode ser criado ou está vazio.",
|
"BackDevices_Backup_Failed": "A copia de segurança foi parcialmente executada. O arquivo não pode ser criado ou está vazio.",
|
||||||
"BackDevices_Backup_okay": "A copia de segurança foi feita executado corretamente com o novo arquivo",
|
"BackDevices_Backup_okay": "A copia de segurança foi feita executado corretamente com o novo arquivo",
|
||||||
@@ -61,14 +61,14 @@
|
|||||||
"BackDevices_Restore_okay": "Restauração executada com sucesso.",
|
"BackDevices_Restore_okay": "Restauração executada com sucesso.",
|
||||||
"BackDevices_darkmode_disabled": "Modo Noturno Desativado",
|
"BackDevices_darkmode_disabled": "Modo Noturno Desativado",
|
||||||
"BackDevices_darkmode_enabled": "Modo Noturno Ativado",
|
"BackDevices_darkmode_enabled": "Modo Noturno Ativado",
|
||||||
"CLEAR_NEW_FLAG_description": "Se ativado (<code>0</code> está desativado), dispositivos marcados como<b>Novo Dispositivo</b> serão desmarcados se o limite (especificado em horas) exceder o tempo da <b>Primeira Sessão </b>.",
|
"CLEAR_NEW_FLAG_description": "Se ativado (<code>0</code> está desativado), dispositivos marcados como <b>Novo Dispositivo</b> serão desmarcados se o limite (especificado em horas) exceder o tempo da <b>Primeira Sessão</b>.",
|
||||||
"CLEAR_NEW_FLAG_name": "Limpar a flag nova",
|
"CLEAR_NEW_FLAG_name": "Limpar a flag nova",
|
||||||
"CustProps_cant_remove": "Não é possível remover, é necessária pelo menos uma propriedade.",
|
"CustProps_cant_remove": "Não é possível remover, é necessária pelo menos uma propriedade.",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Esta é uma definição de manutenção. Especifica o número de dias de entradas de eventos que serão mantidas. Todos os eventos mais antigos serão apagados periodicamente. Também se aplica ao Histórico de eventos do plug-in.",
|
"DAYS_TO_KEEP_EVENTS_description": "Esta é uma definição de manutenção. Especifica o número de dias de entradas de eventos que serão mantidas. Todos os eventos mais antigos serão apagados periodicamente. Também se aplica ao Histórico de eventos do plug-in.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Apagar eventos mais antigos que",
|
"DAYS_TO_KEEP_EVENTS_name": "Apagar eventos mais antigos que",
|
||||||
"DEEP_SLEEP_description": "",
|
"DEEP_SLEEP_description": "Diminui a utilização do CPU ao prolongar tempos de espera ociosos entre ciclos de processamento. Quando ativo, análises podem ser atrasadas por até 1 minuto e o UI pode ficar menos responsivo.",
|
||||||
"DEEP_SLEEP_name": "",
|
"DEEP_SLEEP_name": "Sleep profundo",
|
||||||
"DISCOVER_PLUGINS_description": "Desative esta opção para acelerar a inicialização e a gravação de definições. Quando desativada, os plug-ins não são descobertos e não é possível adicionar novos plug-ins à definição<code>LOADED_PLUGINS</code>.",
|
"DISCOVER_PLUGINS_description": "Desative esta opção para acelerar a inicialização e a gravação de definições. Quando desativada, os plug-ins não são descobertos e não é possível adicionar novos plug-ins à definição <code>LOADED_PLUGINS</code>.",
|
||||||
"DISCOVER_PLUGINS_name": "Descobrir plugins",
|
"DISCOVER_PLUGINS_name": "Descobrir plugins",
|
||||||
"DevDetail_Children_Title": "Relacionamentos de crianças",
|
"DevDetail_Children_Title": "Relacionamentos de crianças",
|
||||||
"DevDetail_Copy_Device_Title": "Copiar pormenores do dispositivo",
|
"DevDetail_Copy_Device_Title": "Copiar pormenores do dispositivo",
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"DevDetail_MainInfo_Location": "Localização",
|
"DevDetail_MainInfo_Location": "Localização",
|
||||||
"DevDetail_MainInfo_Name": "Nome",
|
"DevDetail_MainInfo_Name": "Nome",
|
||||||
"DevDetail_MainInfo_Network": "<i class=\"fa fa-server\"> </i> Node (MAC)",
|
"DevDetail_MainInfo_Network": "<i class=\"fa fa-server\"> </i> Node (MAC)",
|
||||||
"DevDetail_MainInfo_Network_Port": "<i class=\"fa fa-ethernet\"></i>Porta",
|
"DevDetail_MainInfo_Network_Port": "<i class=\"fa fa-ethernet\"></i> Porta",
|
||||||
"DevDetail_MainInfo_Network_Site": "Site",
|
"DevDetail_MainInfo_Network_Site": "Site",
|
||||||
"DevDetail_MainInfo_Network_Title": "Detalhes de Rede",
|
"DevDetail_MainInfo_Network_Title": "Detalhes de Rede",
|
||||||
"DevDetail_MainInfo_Owner": "Proprietário",
|
"DevDetail_MainInfo_Owner": "Proprietário",
|
||||||
@@ -249,8 +249,8 @@
|
|||||||
"Device_TableHead_Name": "Nome",
|
"Device_TableHead_Name": "Nome",
|
||||||
"Device_TableHead_NetworkSite": "Site da rede",
|
"Device_TableHead_NetworkSite": "Site da rede",
|
||||||
"Device_TableHead_Owner": "Proprietário",
|
"Device_TableHead_Owner": "Proprietário",
|
||||||
"Device_TableHead_ParentRelType": "Tipo de relação",
|
"Device_TableHead_ParentRelType": "Relação",
|
||||||
"Device_TableHead_Parent_MAC": "Node de rede anterior",
|
"Device_TableHead_Parent_MAC": "Nó parente",
|
||||||
"Device_TableHead_Port": "Porta",
|
"Device_TableHead_Port": "Porta",
|
||||||
"Device_TableHead_PresentLastScan": "Presença",
|
"Device_TableHead_PresentLastScan": "Presença",
|
||||||
"Device_TableHead_ReqNicsOnline": "Exigir NICs online",
|
"Device_TableHead_ReqNicsOnline": "Exigir NICs online",
|
||||||
@@ -341,7 +341,7 @@
|
|||||||
"Gen_Filter": "Filtro",
|
"Gen_Filter": "Filtro",
|
||||||
"Gen_Flapping": "Flapping",
|
"Gen_Flapping": "Flapping",
|
||||||
"Gen_Generate": "Gerar",
|
"Gen_Generate": "Gerar",
|
||||||
"Gen_InvalidMac": "Endereço MAC Inválido.",
|
"Gen_InvalidMac": "Endereço Mac inválido.",
|
||||||
"Gen_Invalid_Value": "Um valor inválido foi inserido",
|
"Gen_Invalid_Value": "Um valor inválido foi inserido",
|
||||||
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
||||||
"Gen_NetworkMask": "Máscara de Rede",
|
"Gen_NetworkMask": "Máscara de Rede",
|
||||||
@@ -350,7 +350,7 @@
|
|||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
"Gen_Okay": "Ok",
|
"Gen_Okay": "Ok",
|
||||||
"Gen_Online": "Online",
|
"Gen_Online": "Online",
|
||||||
"Gen_Purge": "Purge",
|
"Gen_Purge": "Purgar",
|
||||||
"Gen_ReadDocs": "Leia mais em documentos.",
|
"Gen_ReadDocs": "Leia mais em documentos.",
|
||||||
"Gen_Remove_All": "Remover tudo",
|
"Gen_Remove_All": "Remover tudo",
|
||||||
"Gen_Remove_Last": "Remover o último",
|
"Gen_Remove_Last": "Remover o último",
|
||||||
@@ -403,7 +403,7 @@
|
|||||||
"Login_Toggle_Info_headline": "Informações sobre a palavra-passe",
|
"Login_Toggle_Info_headline": "Informações sobre a palavra-passe",
|
||||||
"Maint_PurgeLog": "Limpar o registo",
|
"Maint_PurgeLog": "Limpar o registo",
|
||||||
"Maint_RestartServer": "Reiniciar o servidor",
|
"Maint_RestartServer": "Reiniciar o servidor",
|
||||||
"Maint_Restart_Server_noti_text": "Tem certeza de que deseja reiniciar o servidor backend? Isto pode causar inconsistência na app. Faça primeiro um backup da sua configuração. <br/> <br/> Nota: Isto pode levar alguns minutos.",
|
"Maint_Restart_Server_noti_text": "Tem a certeza que quer reiniciar o servidor backend? Isto pode causar inconsistências na aplicação. Crie uma cópia de segurança primeiro. <br/><br/> Nota: isto pode demorar alguns minutos.",
|
||||||
"Maintenance_InitCheck": "Verificação inicial",
|
"Maintenance_InitCheck": "Verificação inicial",
|
||||||
"Maintenance_InitCheck_Checking": "A verificar…",
|
"Maintenance_InitCheck_Checking": "A verificar…",
|
||||||
"Maintenance_InitCheck_QuickSetupGuide": "Certifique-se de que seguiu o <a href=\"https://docs.netalertx.com/INITIAL_SETUP/\" target=\"_blank\">guia de configuração rápida</a>.",
|
"Maintenance_InitCheck_QuickSetupGuide": "Certifique-se de que seguiu o <a href=\"https://docs.netalertx.com/INITIAL_SETUP/\" target=\"_blank\">guia de configuração rápida</a>.",
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
"Maintenance_Tool_upgrade_database_noti_text": "Tem certeza de que deseja atualizar a base de dados?<br>(talvez prefira arquivá-la)",
|
"Maintenance_Tool_upgrade_database_noti_text": "Tem certeza de que deseja atualizar a base de dados?<br>(talvez prefira arquivá-la)",
|
||||||
"Maintenance_Tool_upgrade_database_text": "Este botão atualizará a base de dados para ativar o gráfico Atividade de rede nas últimas 12 horas. Faça uma cópia de segurança da sua base de dados em caso de problemas.",
|
"Maintenance_Tool_upgrade_database_text": "Este botão atualizará a base de dados para ativar o gráfico Atividade de rede nas últimas 12 horas. Faça uma cópia de segurança da sua base de dados em caso de problemas.",
|
||||||
"Maintenance_Tools_Tab_BackupRestore": "Backup / Restauração",
|
"Maintenance_Tools_Tab_BackupRestore": "Backup / Restauração",
|
||||||
"Maintenance_Tools_Tab_Logging": "Logs",
|
"Maintenance_Tools_Tab_Logging": "Registos",
|
||||||
"Maintenance_Tools_Tab_Settings": "Configurações",
|
"Maintenance_Tools_Tab_Settings": "Configurações",
|
||||||
"Maintenance_Tools_Tab_Tools": "Ferramentas",
|
"Maintenance_Tools_Tab_Tools": "Ferramentas",
|
||||||
"Maintenance_Tools_Tab_UISettings": "Configurações de interface",
|
"Maintenance_Tools_Tab_UISettings": "Configurações de interface",
|
||||||
@@ -503,7 +503,7 @@
|
|||||||
"Maintenance_arp_status_off": "está atualmente desativado",
|
"Maintenance_arp_status_off": "está atualmente desativado",
|
||||||
"Maintenance_arp_status_on": "Scan em curso",
|
"Maintenance_arp_status_on": "Scan em curso",
|
||||||
"Maintenance_built_on": "Construído em",
|
"Maintenance_built_on": "Construído em",
|
||||||
"Maintenance_current_version": "Você está atualizado. Confira o que <a href=\"https://github.com/netalertx/NetAlertX/issues/138\" target=\"_blank\"> estou a trabalhar em</a>.",
|
"Maintenance_current_version": "Você está atualizado. Confira no que é que <a href=\"https://github.com/netalertx/NetAlertX/issues/138\" target=\"_blank\">estou a trabalhar em</a>.",
|
||||||
"Maintenance_database_backup": "Backups DB",
|
"Maintenance_database_backup": "Backups DB",
|
||||||
"Maintenance_database_backup_found": "foram encontrados backups",
|
"Maintenance_database_backup_found": "foram encontrados backups",
|
||||||
"Maintenance_database_backup_total": "uso total do disco",
|
"Maintenance_database_backup_total": "uso total do disco",
|
||||||
@@ -538,7 +538,7 @@
|
|||||||
"Navigation_Report": "Reports enviados",
|
"Navigation_Report": "Reports enviados",
|
||||||
"Navigation_Settings": "Definições",
|
"Navigation_Settings": "Definições",
|
||||||
"Navigation_SystemInfo": "Informação de sistema",
|
"Navigation_SystemInfo": "Informação de sistema",
|
||||||
"Navigation_Workflows": "Workflows",
|
"Navigation_Workflows": "Fluxos de Trabalho",
|
||||||
"Network_Assign": "Conectar ao nodo de network <i class=\"fa fa-server\"></i> em cima",
|
"Network_Assign": "Conectar ao nodo de network <i class=\"fa fa-server\"></i> em cima",
|
||||||
"Network_Cant_Assign": "Não é possível atribuir o node raiz da Internet como um node folha filho.",
|
"Network_Cant_Assign": "Não é possível atribuir o node raiz da Internet como um node folha filho.",
|
||||||
"Network_Cant_Assign_No_Node_Selected": "Não é possível atribuir, nenhum node pai selecionado.",
|
"Network_Cant_Assign_No_Node_Selected": "Não é possível atribuir, nenhum node pai selecionado.",
|
||||||
@@ -565,13 +565,13 @@
|
|||||||
"Network_ManageEdit_Name": "Novo nome de dispositivo",
|
"Network_ManageEdit_Name": "Novo nome de dispositivo",
|
||||||
"Network_ManageEdit_Name_text": "Nome sem caracteres especiais",
|
"Network_ManageEdit_Name_text": "Nome sem caracteres especiais",
|
||||||
"Network_ManageEdit_Port": " Nova contagem de portas",
|
"Network_ManageEdit_Port": " Nova contagem de portas",
|
||||||
"Network_ManageEdit_Port_text": "Deixe em branco para Wi-Fi e Powerline.",
|
"Network_ManageEdit_Port_text": "Deixe em branco para Wi-Fi e Powerline",
|
||||||
"Network_ManageEdit_Submit": "Guardar Alterações",
|
"Network_ManageEdit_Submit": "Guardar Alterações",
|
||||||
"Network_ManageEdit_Type": "Novo tipo de dispositivo",
|
"Network_ManageEdit_Type": "Novo tipo de dispositivo",
|
||||||
"Network_ManageEdit_Type_text": "-- Selecionar tipo --",
|
"Network_ManageEdit_Type_text": "-- Selecionar tipo --",
|
||||||
"Network_ManageLeaf": "Gerir atribuição",
|
"Network_ManageLeaf": "Gerir atribuição",
|
||||||
"Network_ManageUnassign": "Cancelar Atribuição",
|
"Network_ManageUnassign": "Cancelar Atribuição",
|
||||||
"Network_NoAssignedDevices": "Este nó de rede não tem quaisquer dispositivos atribuídos (nós folha). Atribua um abaixo ou vá ao separador <b><i class=\"fa fa-info-circle\"> Detalhes</b> em qualquer dispositivo em <a href=\"devices.php\"><b><i class=\"fa fa-laptop\"></i> Dispositivos</b></a>, e atribua-o a um <i <b><i class=\"fa fa-server\"></i> Nó de rede (MAC)</b> e <b><i class=\"fa fa-ethernet\"></i> Porta</b> lá.",
|
"Network_NoAssignedDevices": "Este nó de rede não tem quaisquer dispositivos atribuídos (nós folha). Atribua um abaixo ou vá à aba <b><i class=\"fa fa-info-circle\"></i> Detalhes</b> de qualquer dispositivo em <a href=\"devices.php\"><b> <i class=\"fa fa-laptop\"></i> Dispositivos</b></a>, e atribua os mesmos a uma rede <b><i class=\"fa fa-server\"></i> Nó (MAC)</b> e <b><i class=\"fa fa-ethernet\"></i> Porta</b> lá.",
|
||||||
"Network_NoDevices": "Sem dispositivos para configurar",
|
"Network_NoDevices": "Sem dispositivos para configurar",
|
||||||
"Network_Node": "Nó de rede",
|
"Network_Node": "Nó de rede",
|
||||||
"Network_Node_Name": "Nome do nó",
|
"Network_Node_Name": "Nome do nó",
|
||||||
@@ -629,183 +629,183 @@
|
|||||||
"REFRESH_FQDN_description": "Reanalisa todos os dispositivos e atualiza o seu Nome de Domínio Qualificado Completo (FQDN). Se estiver desativado, apenas dispositivos sem um nome conhecido serão analisados para melhorar o desempenho. Neste caso, FQDN é atualizado apenas durante a descoberta de dispositivos inicial.",
|
"REFRESH_FQDN_description": "Reanalisa todos os dispositivos e atualiza o seu Nome de Domínio Qualificado Completo (FQDN). Se estiver desativado, apenas dispositivos sem um nome conhecido serão analisados para melhorar o desempenho. Neste caso, FQDN é atualizado apenas durante a descoberta de dispositivos inicial.",
|
||||||
"REFRESH_FQDN_name": "Atualizar FQDN",
|
"REFRESH_FQDN_name": "Atualizar FQDN",
|
||||||
"REPORT_DASHBOARD_URL_description": "Este URL é usado como base para gerar links nos relatórios HTML (p.ex.: emails). Introduza o URL começado com <code>http://</code> incluindo o número da porta (sem barra final <code>/</code>).",
|
"REPORT_DASHBOARD_URL_description": "Este URL é usado como base para gerar links nos relatórios HTML (p.ex.: emails). Introduza o URL começado com <code>http://</code> incluindo o número da porta (sem barra final <code>/</code>).",
|
||||||
"REPORT_DASHBOARD_URL_name": "",
|
"REPORT_DASHBOARD_URL_name": "URL NetAlertX",
|
||||||
"REPORT_ERROR": "",
|
"REPORT_ERROR": "A página que procura está indisponível temporariamente, por favor tente outra vez após alguns segundos",
|
||||||
"REPORT_MAIL_description": "",
|
"REPORT_MAIL_description": "Se ativo, um email é enviado com uma lista de mudanças às quais subscreveu. Por favor preencha também todas as definições relacionadas com a configuração SMTP abaixo. Se está a encontrar problemas, defina <code>LOG_LEVEL</code> para <code>debug</code> e verifique o <a href=\"/maintenance.php#tab_Logging\">registo de erros</a>.",
|
||||||
"REPORT_MAIL_name": "",
|
"REPORT_MAIL_name": "Ativar email",
|
||||||
"REPORT_TITLE": "",
|
"REPORT_TITLE": "Reportar",
|
||||||
"RandomMAC_hover": "",
|
"RandomMAC_hover": "Este dispositivo tem um endereço MAC aleatório",
|
||||||
"Reports_Sent_Log": "",
|
"Reports_Sent_Log": "Registo de relatórios enviado",
|
||||||
"SCAN_SUBNETS_description": "",
|
"SCAN_SUBNETS_description": "A maior parte dos scanners on-network (ARP-SCAN, NMAP, NSLOOKUP, DIG) baseiam-se em scanear interfaces de rede específicas e subredes. Veja a <a href=\"https://docs.netalertx.com/SUBNETS\" target=\"_blank\">documentação de subredes</a> para ajudar com esta definição, especialmente VLANs, quais VLANs são suportadas, ou como descobrir a máscara de rede e a sua interface. <br/> <br/> Uma alternativa a scanners on-network é ativar outro scanner de dispositivos/importadores que não dependam do NetAlert<sup>X</sup> tenha acesso à rede (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Nota: O tempo de scaneamento em si depende do número de endereços de IP a verificar, por isso configure isto com cuidado com a máscara e interface de rede apropriadas.",
|
||||||
"SCAN_SUBNETS_name": "",
|
"SCAN_SUBNETS_name": "Redes a scanear",
|
||||||
"SYSTEM_TITLE": "",
|
"SYSTEM_TITLE": "Informação de Sistema",
|
||||||
"Setting_Override": "",
|
"Setting_Override": "Sobrescrever valor",
|
||||||
"Setting_Override_Description": "",
|
"Setting_Override_Description": "Ativar esta opção irá sobrescrever o valor predefinido pela App com o valor especificado acima.",
|
||||||
"Settings_Metadata_Toggle": "",
|
"Settings_Metadata_Toggle": "Mostrar/esconder metadados para definição especificada.",
|
||||||
"Settings_Show_Description": "",
|
"Settings_Show_Description": "Mostrar descrição",
|
||||||
"Settings_device_Scanners_desync": "",
|
"Settings_device_Scanners_desync": "⚠ Os horários de procura de dispositivos estão dessincronizados.",
|
||||||
"Settings_device_Scanners_desync_popup": "",
|
"Settings_device_Scanners_desync_popup": "Horários de scanners de dispositivos (<code>*_RUN_SCHD</code>) não são o mesmo. Isto resultará em notificações de dispositivo online/offline inconsistentes. A menos que isto seja intencional, por favor use o mesmo horário para todos os <b>🔍scanners de dispositivos</b> ativos.",
|
||||||
"Speedtest_Results": "",
|
"Speedtest_Results": "Resultados do Teste de Velocidade",
|
||||||
"Systeminfo_AvailableIps": "",
|
"Systeminfo_AvailableIps": "IPs Disponíveis",
|
||||||
"Systeminfo_CPU": "",
|
"Systeminfo_CPU": "CPU",
|
||||||
"Systeminfo_CPU_Cores": "",
|
"Systeminfo_CPU_Cores": "Cores CPU:",
|
||||||
"Systeminfo_CPU_Name": "",
|
"Systeminfo_CPU_Name": "Nome do CPU:",
|
||||||
"Systeminfo_CPU_Speed": "",
|
"Systeminfo_CPU_Speed": "Velocidade do CPU:",
|
||||||
"Systeminfo_CPU_Temp": "",
|
"Systeminfo_CPU_Temp": "Temperatura do CPU:",
|
||||||
"Systeminfo_CPU_Vendor": "",
|
"Systeminfo_CPU_Vendor": "Fornecedor do CPU:",
|
||||||
"Systeminfo_Client_Resolution": "",
|
"Systeminfo_Client_Resolution": "Resolução do Browser:",
|
||||||
"Systeminfo_Client_User_Agent": "",
|
"Systeminfo_Client_User_Agent": "Agente do Utilizador:",
|
||||||
"Systeminfo_General": "",
|
"Systeminfo_General": "Geral",
|
||||||
"Systeminfo_General_Date": "",
|
"Systeminfo_General_Date": "Data:",
|
||||||
"Systeminfo_General_Date2": "",
|
"Systeminfo_General_Date2": "Data2:",
|
||||||
"Systeminfo_General_Full_Date": "",
|
"Systeminfo_General_Full_Date": "Data Completa:",
|
||||||
"Systeminfo_General_TimeZone": "",
|
"Systeminfo_General_TimeZone": "Fuso Horário:",
|
||||||
"Systeminfo_Memory": "",
|
"Systeminfo_Memory": "Memória",
|
||||||
"Systeminfo_Memory_Total_Memory": "",
|
"Systeminfo_Memory_Total_Memory": "Memória total:",
|
||||||
"Systeminfo_Memory_Usage": "",
|
"Systeminfo_Memory_Usage": "Utilização de memória:",
|
||||||
"Systeminfo_Memory_Usage_Percent": "",
|
"Systeminfo_Memory_Usage_Percent": "Memória %:",
|
||||||
"Systeminfo_Motherboard": "",
|
"Systeminfo_Motherboard": "Motherboard",
|
||||||
"Systeminfo_Motherboard_BIOS": "",
|
"Systeminfo_Motherboard_BIOS": "BIOS:",
|
||||||
"Systeminfo_Motherboard_BIOS_Date": "",
|
"Systeminfo_Motherboard_BIOS_Date": "Data da BIOS:",
|
||||||
"Systeminfo_Motherboard_BIOS_Vendor": "",
|
"Systeminfo_Motherboard_BIOS_Vendor": "Fabricante da BIOS:",
|
||||||
"Systeminfo_Motherboard_Manufactured": "",
|
"Systeminfo_Motherboard_Manufactured": "Fabricado por:",
|
||||||
"Systeminfo_Motherboard_Name": "",
|
"Systeminfo_Motherboard_Name": "Nome:",
|
||||||
"Systeminfo_Motherboard_Revision": "",
|
"Systeminfo_Motherboard_Revision": "Revisão:",
|
||||||
"Systeminfo_Network": "",
|
"Systeminfo_Network": "Rede",
|
||||||
"Systeminfo_Network_Accept_Encoding": "",
|
"Systeminfo_Network_Accept_Encoding": "Aceitar codificação:",
|
||||||
"Systeminfo_Network_Accept_Language": "",
|
"Systeminfo_Network_Accept_Language": "Aceitar idioma:",
|
||||||
"Systeminfo_Network_Connection_Port": "",
|
"Systeminfo_Network_Connection_Port": "Porta de conexão:",
|
||||||
"Systeminfo_Network_HTTP_Host": "",
|
"Systeminfo_Network_HTTP_Host": "Anfitrião HTTP:",
|
||||||
"Systeminfo_Network_HTTP_Referer": "",
|
"Systeminfo_Network_HTTP_Referer": "Referenciador HTTP:",
|
||||||
"Systeminfo_Network_HTTP_Referer_String": "",
|
"Systeminfo_Network_HTTP_Referer_String": "Nenhum referenciador HTTP",
|
||||||
"Systeminfo_Network_Hardware": "",
|
"Systeminfo_Network_Hardware": "Hardware de Rede",
|
||||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
"Systeminfo_Network_Hardware_Interface_Mask": "Máscara de Rede",
|
||||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
"Systeminfo_Network_Hardware_Interface_Name": "Nome da Interface",
|
||||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
"Systeminfo_Network_Hardware_Interface_RX": "Recebido",
|
||||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
"Systeminfo_Network_Hardware_Interface_TX": "Transmitido",
|
||||||
"Systeminfo_Network_IP": "",
|
"Systeminfo_Network_IP": "Internet IP:",
|
||||||
"Systeminfo_Network_IP_Connection": "",
|
"Systeminfo_Network_IP_Connection": "Conexão IP:",
|
||||||
"Systeminfo_Network_IP_Server": "",
|
"Systeminfo_Network_IP_Server": "IP do Servidor:",
|
||||||
"Systeminfo_Network_MIME": "",
|
"Systeminfo_Network_MIME": "MIME:",
|
||||||
"Systeminfo_Network_Request_Method": "",
|
"Systeminfo_Network_Request_Method": "Método do Pedido:",
|
||||||
"Systeminfo_Network_Request_Time": "",
|
"Systeminfo_Network_Request_Time": "Tempo do pedido:",
|
||||||
"Systeminfo_Network_Request_URI": "",
|
"Systeminfo_Network_Request_URI": "URI do Pedido:",
|
||||||
"Systeminfo_Network_Secure_Connection": "",
|
"Systeminfo_Network_Secure_Connection": "Ligação segura:",
|
||||||
"Systeminfo_Network_Secure_Connection_String": "",
|
"Systeminfo_Network_Secure_Connection_String": "Sem (HTTP)",
|
||||||
"Systeminfo_Network_Server_Name": "",
|
"Systeminfo_Network_Server_Name": "Nome do servidor:",
|
||||||
"Systeminfo_Network_Server_Name_String": "",
|
"Systeminfo_Network_Server_Name_String": "Nome de servidor não encontrado",
|
||||||
"Systeminfo_Network_Server_Query": "",
|
"Systeminfo_Network_Server_Query": "Consulta de servidor:",
|
||||||
"Systeminfo_Network_Server_Query_String": "",
|
"Systeminfo_Network_Server_Query_String": "Nenhuma string de consulta",
|
||||||
"Systeminfo_Network_Server_Version": "",
|
"Systeminfo_Network_Server_Version": "Versão do Servidor:",
|
||||||
"Systeminfo_Services": "",
|
"Systeminfo_Services": "Serviços",
|
||||||
"Systeminfo_Services_Description": "",
|
"Systeminfo_Services_Description": "Descrição do Serviço",
|
||||||
"Systeminfo_Services_Name": "",
|
"Systeminfo_Services_Name": "Nome do Serviço",
|
||||||
"Systeminfo_Storage": "",
|
"Systeminfo_Storage": "Armazenamento",
|
||||||
"Systeminfo_Storage_Device": "",
|
"Systeminfo_Storage_Device": "Dispositivo:",
|
||||||
"Systeminfo_Storage_Mount": "",
|
"Systeminfo_Storage_Mount": "Ponto de montagem:",
|
||||||
"Systeminfo_Storage_Size": "",
|
"Systeminfo_Storage_Size": "Tamanho:",
|
||||||
"Systeminfo_Storage_Type": "",
|
"Systeminfo_Storage_Type": "Tipo:",
|
||||||
"Systeminfo_Storage_Usage": "",
|
"Systeminfo_Storage_Usage": "Utilização de armazenamento",
|
||||||
"Systeminfo_Storage_Usage_Free": "",
|
"Systeminfo_Storage_Usage_Free": "Livre:",
|
||||||
"Systeminfo_Storage_Usage_Mount": "",
|
"Systeminfo_Storage_Usage_Mount": "Ponto de montagem:",
|
||||||
"Systeminfo_Storage_Usage_Total": "",
|
"Systeminfo_Storage_Usage_Total": "Total:",
|
||||||
"Systeminfo_Storage_Usage_Used": "",
|
"Systeminfo_Storage_Usage_Used": "Usado:",
|
||||||
"Systeminfo_System": "",
|
"Systeminfo_System": "Sistema",
|
||||||
"Systeminfo_System_AVG": "",
|
"Systeminfo_System_AVG": "Média de carregamento:",
|
||||||
"Systeminfo_System_Architecture": "",
|
"Systeminfo_System_Architecture": "Arquitetura:",
|
||||||
"Systeminfo_System_Kernel": "",
|
"Systeminfo_System_Kernel": "Kernel:",
|
||||||
"Systeminfo_System_OSVersion": "",
|
"Systeminfo_System_OSVersion": "Sistema Operativo:",
|
||||||
"Systeminfo_System_Running_Processes": "",
|
"Systeminfo_System_Running_Processes": "Processos em execução:",
|
||||||
"Systeminfo_System_System": "",
|
"Systeminfo_System_System": "Sistema:",
|
||||||
"Systeminfo_System_Uname": "",
|
"Systeminfo_System_Uname": "Uname:",
|
||||||
"Systeminfo_System_Uptime": "",
|
"Systeminfo_System_Uptime": "Tempo de atividade:",
|
||||||
"Systeminfo_This_Client": "",
|
"Systeminfo_This_Client": "Este Cliente",
|
||||||
"Systeminfo_USB_Devices": "",
|
"Systeminfo_USB_Devices": "Dispositivos USB",
|
||||||
"TICKER_MIGRATE_TO_NETALERTX": "",
|
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Antigos locais de montagem detetados. Siga <a href=\"https://docs.netalertx.com/MIGRATION\" target=\"_blank\">este guia</a> para migrar as novas pastas <code>/data/config</code> e <code>/data/db</code> e o contentor <code>netalertx</code>.",
|
||||||
"TIMEZONE_description": "",
|
"TIMEZONE_description": "Fuso horário para mostras estatísticas corretamente. Encontre o seu fuso horário <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aqui</a>.",
|
||||||
"TIMEZONE_name": "",
|
"TIMEZONE_name": "Fuso horário",
|
||||||
"UI_DEV_SECTIONS_description": "",
|
"UI_DEV_SECTIONS_description": "Selecione quais elementos UI a esconder na página de dispositivos.",
|
||||||
"UI_DEV_SECTIONS_name": "",
|
"UI_DEV_SECTIONS_name": "Esconder secções de dispositivos",
|
||||||
"UI_ICONS_description": "",
|
"UI_ICONS_description": "Uma lista de ícones pré-definidos. Proceda com cautela, a maneira preferível de adicionar ícones é descrita na <a href=\"https://docs.netalertx.com/ICONS\" target=\"_blank\">Documentação de ícones</a>. Pode adicionar uma etiqueta codificada em base64 SVG HTML ou Font-awesome HTML.",
|
||||||
"UI_ICONS_name": "",
|
"UI_ICONS_name": "Ícones pré-definidos",
|
||||||
"UI_LANG_description": "",
|
"UI_LANG_description": "Selecione o idioma de UI preferido. Ajude a traduzir ou sugira idiomas no portal online do <a href=\"https://hosted.weblate.org/projects/pialert/core/\" target=\"_blank\">Weblate</a>.",
|
||||||
"UI_LANG_name": "",
|
"UI_LANG_name": "Idioma do UI",
|
||||||
"UI_MY_DEVICES_description": "",
|
"UI_MY_DEVICES_description": "Dispositivos cujo estado devem ser mostrados na vista predefinida <b>Os meus dispositivos</b>.",
|
||||||
"UI_MY_DEVICES_name": "",
|
"UI_MY_DEVICES_name": "Mostrar na vista Os meus dispositivos",
|
||||||
"UI_NOT_RANDOM_MAC_description": "",
|
"UI_NOT_RANDOM_MAC_description": "Prefixos mac que não devem ser marcados como dispositivos Aleatórios. Introduza, por exemplo, <code>52</code> para excluir dispositivos que começam com <code>52:xx:xx:xx:xx:xx</code> de serem marcados como dispositivos com um endereço MAC aleatório.",
|
||||||
"UI_NOT_RANDOM_MAC_name": "",
|
"UI_NOT_RANDOM_MAC_name": "Não marcar como Aleatório",
|
||||||
"UI_PRESENCE_description": "",
|
"UI_PRESENCE_description": "Selecione quais estatutos devem ser mostrados na <b>Presença de dispositivo</b> gráfico na página de <a href=\"/devices.php\" target=\"_blank\">Dispositivos</a>.",
|
||||||
"UI_PRESENCE_name": "",
|
"UI_PRESENCE_name": "Mostrar em gráfico de presença",
|
||||||
"UI_REFRESH_description": "",
|
"UI_REFRESH_description": "Introduza o número de segundo após os quais o UI atualiza. Defina para <code>0</code> para desativar.",
|
||||||
"UI_REFRESH_name": "",
|
"UI_REFRESH_name": "Auto-atualizar UI",
|
||||||
"VERSION_description": "",
|
"VERSION_description": "Valor auxiliar de versão ou data e hora para verificar se a aplicação foi atualizada.",
|
||||||
"VERSION_name": "",
|
"VERSION_name": "Versão ou data e hora",
|
||||||
"WF_Action_Add": "",
|
"WF_Action_Add": "Adicionar Ação",
|
||||||
"WF_Action_field": "",
|
"WF_Action_field": "Campo",
|
||||||
"WF_Action_type": "",
|
"WF_Action_type": "Tipo",
|
||||||
"WF_Action_value": "",
|
"WF_Action_value": "Valor",
|
||||||
"WF_Actions": "",
|
"WF_Actions": "Ações",
|
||||||
"WF_Add": "",
|
"WF_Add": "Adicionar Fluxo de Trabalho",
|
||||||
"WF_Add_Condition": "",
|
"WF_Add_Condition": "Adicionar Condição",
|
||||||
"WF_Add_Group": "",
|
"WF_Add_Group": "Adicionar Grupo",
|
||||||
"WF_Condition_field": "",
|
"WF_Condition_field": "Campo",
|
||||||
"WF_Condition_operator": "",
|
"WF_Condition_operator": "Operador",
|
||||||
"WF_Condition_value": "",
|
"WF_Condition_value": "Valor",
|
||||||
"WF_Conditions": "",
|
"WF_Conditions": "Condições",
|
||||||
"WF_Conditions_logic_rules": "",
|
"WF_Conditions_logic_rules": "Regras de lógica",
|
||||||
"WF_Duplicate": "",
|
"WF_Duplicate": "Duplicar Fluxo de Trabalho",
|
||||||
"WF_Enabled": "",
|
"WF_Enabled": "Fluxo de trabalho ativo",
|
||||||
"WF_Export": "",
|
"WF_Export": "Exportar Fluxo de Trabalho",
|
||||||
"WF_Export_Copy": "",
|
"WF_Export_Copy": "Copiar para o fluxo de trabalho abaixo e importar para onde for preciso.",
|
||||||
"WF_Import": "",
|
"WF_Import": "Importar Fluxo de Trabalho",
|
||||||
"WF_Import_Copy": "",
|
"WF_Import_Copy": "Colar no fluxo de trabalho que copiou anteriormente.",
|
||||||
"WF_Name": "",
|
"WF_Name": "Nome do fluxo de trabalho",
|
||||||
"WF_Remove": "",
|
"WF_Remove": "Remover Fluxo de Trabalho",
|
||||||
"WF_Remove_Copy": "",
|
"WF_Remove_Copy": "Quer remover este fluxo de trabalho?",
|
||||||
"WF_Save": "",
|
"WF_Save": "Guardar Fluxos de Trabalho",
|
||||||
"WF_Trigger": "",
|
"WF_Trigger": "Acionador",
|
||||||
"WF_Trigger_event_type": "",
|
"WF_Trigger_event_type": "Tipo de evento",
|
||||||
"WF_Trigger_type": "",
|
"WF_Trigger_type": "Tipo de acionador",
|
||||||
"add_icon_event_tooltip": "",
|
"add_icon_event_tooltip": "Adicionar novo ícone",
|
||||||
"add_option_event_tooltip": "",
|
"add_option_event_tooltip": "Adicionar novo valor",
|
||||||
"copy_icons_event_tooltip": "",
|
"copy_icons_event_tooltip": "Sobrescrever ícones de todos os dispositivos com o mesmo tipo de dispositivo",
|
||||||
"devices_old": "",
|
"devices_old": "A atualizar…",
|
||||||
"general_event_description": "",
|
"general_event_description": "O evento que ativou pode demorar algum tempo até que os processos de planos de fundo terminem. A execução acabou uma vez que a lista de execução abaixo fica vazia (Verifique o <a href='/maintenance.php#tab_Logging'>registo de erros</a> se encontrar problemas). <br/> <br/> Lista de execução:",
|
||||||
"general_event_title": "",
|
"general_event_title": "A executar um evento ad-hoc",
|
||||||
"go_to_device_event_tooltip": "",
|
"go_to_device_event_tooltip": "Navegar para o dispositivo",
|
||||||
"go_to_node_event_tooltip": "",
|
"go_to_node_event_tooltip": "Navegar para a página de Rede do nó em questão",
|
||||||
"new_version_available": "",
|
"new_version_available": "Uma versão nova está disponível.",
|
||||||
"report_guid": "",
|
"report_guid": "Guid de Notificação:",
|
||||||
"report_guid_missing": "",
|
"report_guid_missing": "Notificação associada não foi encontrada. Há um pequeno atraso entre notificações recentemente enviadas e as mesmas estarem disponíveis. Atualize a sua página e cache após alguns segundos. Também é possível que a notificação selecionada tenha sido eliminada durante a manutenção como especificado na definição <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Em vez disso, a última notificação é mostrada. A notificação em falta tem o seguinte GUID:",
|
||||||
"report_select_format": "",
|
"report_select_format": "Selecionar Formato:",
|
||||||
"report_time": "",
|
"report_time": "Tempo de notificação:",
|
||||||
"run_event_tooltip": "",
|
"run_event_tooltip": "Ative a definição e guarde as suas mudanças primeiro antes de corrê-lo.",
|
||||||
"select_icon_event_tooltip": "",
|
"select_icon_event_tooltip": "Selecionar ícone",
|
||||||
"settings_core_icon": "",
|
"settings_core_icon": "fa-solid fa-gem",
|
||||||
"settings_core_label": "",
|
"settings_core_label": "Core",
|
||||||
"settings_device_scanners": "",
|
"settings_device_scanners": "Scaneadores de dispositivos usados para descobrir dispositivos que escrevem para a tabela da base de dados CurrentScan.",
|
||||||
"settings_device_scanners_icon": "",
|
"settings_device_scanners_icon": "fa-solid fa-magnifying-glass-plus",
|
||||||
"settings_device_scanners_info": "",
|
"settings_device_scanners_info": "Carregar mais scaneadores de dispositivos com a definição <a href=\"/settings.php#LOADED_PLUGINS\">LOADED_PLUGINS</a>",
|
||||||
"settings_device_scanners_label": "",
|
"settings_device_scanners_label": "Scaneadores de dispositivos",
|
||||||
"settings_enabled": "",
|
"settings_enabled": "Definições ativas",
|
||||||
"settings_enabled_icon": "",
|
"settings_enabled_icon": "fa-solid fa-toggle-on",
|
||||||
"settings_expand_all": "",
|
"settings_expand_all": "Expandir todos",
|
||||||
"settings_imported": "",
|
"settings_imported": "Na última vez, as definições foram importadas a partir do ficheiro app.conf",
|
||||||
"settings_imported_label": "",
|
"settings_imported_label": "Definições importadas",
|
||||||
"settings_missing": "",
|
"settings_missing": "Nem todas as configurações foram carregadas! Carga elevada na base de dados ou na sequência de começo da aplicação. Clique no botão 🔄 de atualizar no topo.",
|
||||||
"settings_missing_block": "",
|
"settings_missing_block": "Erro: Definições não carregadas corretamente. Clique no botão 🔄 de atualizar no topo ou, alternativamente, verifique o registo do navegador para detalhes (F12).",
|
||||||
"settings_old": "",
|
"settings_old": "A importar definições e a reinicializar…",
|
||||||
"settings_other_scanners": "",
|
"settings_other_scanners": "Outros plugins de scaneadores que não são do dispositivo estão atualmente ativos.",
|
||||||
"settings_other_scanners_icon": "",
|
"settings_other_scanners_icon": "fa-solid fa-recycle",
|
||||||
"settings_other_scanners_label": "",
|
"settings_other_scanners_label": "Outros scaneadores",
|
||||||
"settings_publishers": "",
|
"settings_publishers": "Gateways de notificação ativados - editores que enviarão uma notificação de acordo com as suas definições.",
|
||||||
"settings_publishers_icon": "",
|
"settings_publishers_icon": "fa-solid fa-paper-plane",
|
||||||
"settings_publishers_info": "",
|
"settings_publishers_info": "Carregar mais Editores com a definição <a href=\"/settings.php#LOADED_PLUGINS\">LOADED_PLUGINS</a>",
|
||||||
"settings_publishers_label": "",
|
"settings_publishers_label": "Editores",
|
||||||
"settings_readonly": "",
|
"settings_readonly": "Não foi possível LÊR ou ESCREVER na <code>app.conf</code>. Tente reiniciar o contentoer e ler a <a href=\"https://docs.netalertx.com/FILE_PERMISSIONS\" target=\"_blank\">documentação de permissões de ficheiro</a>",
|
||||||
"settings_saved": "",
|
"settings_saved": "<br/>Definições guardadas.<br/> A recarregar...<br/><i class=\"ion ion-ios-loop-strong fa-spin fa-2x fa-fw\"></i><br/>",
|
||||||
"settings_system_icon": "",
|
"settings_system_icon": "fa-solid fa-gear",
|
||||||
"settings_system_label": "",
|
"settings_system_label": "Sistema",
|
||||||
"settings_update_item_warning": "",
|
"settings_update_item_warning": "Atualize o valor abaixo. Tenha cuidado em seguir o formato anterior. <b>Validação não é efetuada.</b>",
|
||||||
"test_event_tooltip": "Guarde as alterações antes de testar as definições."
|
"test_event_tooltip": "Guarde as alterações antes de testar as definições."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"AppEvents_ObjectType": "Тип объекта",
|
"AppEvents_ObjectType": "Тип объекта",
|
||||||
"AppEvents_Plugin": "Плагин",
|
"AppEvents_Plugin": "Плагин",
|
||||||
"AppEvents_Type": "Тип",
|
"AppEvents_Type": "Тип",
|
||||||
"BACKEND_API_URL_description": "Используется для обеспечения связи между фронтендом и бэкендом. По умолчанию это значение установлено на <code>/server</code> и, как правило, не должно изменяться.",
|
"BACKEND_API_URL_description": "Используется для обеспечения связи между фронтендом и бэкендом. По умолчанию это значение установлено на <code>/server</code> и, как правило, не должно изменяться.",
|
||||||
"BACKEND_API_URL_name": "URL-адрес серверного API",
|
"BACKEND_API_URL_name": "URL-адрес серверного API",
|
||||||
"BackDevDetail_Actions_Ask_Run": "Вы хотите выполнить действие?",
|
"BackDevDetail_Actions_Ask_Run": "Вы хотите выполнить действие?",
|
||||||
"BackDevDetail_Actions_Not_Registered": "Действие не зарегистрировано:· ",
|
"BackDevDetail_Actions_Not_Registered": "Действие не зарегистрировано:· ",
|
||||||
|
|||||||
@@ -249,8 +249,8 @@
|
|||||||
"Device_TableHead_Name": "名字",
|
"Device_TableHead_Name": "名字",
|
||||||
"Device_TableHead_NetworkSite": "网络站点",
|
"Device_TableHead_NetworkSite": "网络站点",
|
||||||
"Device_TableHead_Owner": "所有者",
|
"Device_TableHead_Owner": "所有者",
|
||||||
"Device_TableHead_ParentRelType": "关系类型",
|
"Device_TableHead_ParentRelType": "关系",
|
||||||
"Device_TableHead_Parent_MAC": "父网络节点",
|
"Device_TableHead_Parent_MAC": "父节点",
|
||||||
"Device_TableHead_Port": "端口",
|
"Device_TableHead_Port": "端口",
|
||||||
"Device_TableHead_PresentLastScan": "检测",
|
"Device_TableHead_PresentLastScan": "检测",
|
||||||
"Device_TableHead_ReqNicsOnline": "需要网卡在线",
|
"Device_TableHead_ReqNicsOnline": "需要网卡在线",
|
||||||
@@ -407,7 +407,7 @@
|
|||||||
"Maintenance_InitCheck": "初步检查",
|
"Maintenance_InitCheck": "初步检查",
|
||||||
"Maintenance_InitCheck_Checking": "查看中…",
|
"Maintenance_InitCheck_Checking": "查看中…",
|
||||||
"Maintenance_InitCheck_QuickSetupGuide": "确保您遵循<a href=\"https://docs.netalertx.com/INITIAL_SETUP/\" target=\"_blank\">快速设置指南</a>。",
|
"Maintenance_InitCheck_QuickSetupGuide": "确保您遵循<a href=\"https://docs.netalertx.com/INITIAL_SETUP/\" target=\"_blank\">快速设置指南</a>。",
|
||||||
"Maintenance_InitCheck_Success": "应用程序启动成功!",
|
"Maintenance_InitCheck_Success": "应用程序初始化成功!",
|
||||||
"Maintenance_ReCheck": "重试检查",
|
"Maintenance_ReCheck": "重试检查",
|
||||||
"Maintenance_Running_Version": "安装版本",
|
"Maintenance_Running_Version": "安装版本",
|
||||||
"Maintenance_Status": "状态",
|
"Maintenance_Status": "状态",
|
||||||
@@ -808,4 +808,4 @@
|
|||||||
"settings_system_label": "系统",
|
"settings_system_label": "系统",
|
||||||
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
|
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
|
||||||
"test_event_tooltip": "在测试设置之前,请先保存更改。"
|
"test_event_tooltip": "在测试设置之前,请先保存更改。"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default_value": "before_name_updates",
|
"default_value": "disabled",
|
||||||
"options": [
|
"options": [
|
||||||
"disabled",
|
"disabled",
|
||||||
"before_name_updates",
|
"before_name_updates",
|
||||||
|
|||||||
@@ -209,53 +209,83 @@ def execute_fping(timeout, args, all_devices, plugin_objects, subnets, interface
|
|||||||
|
|
||||||
def run_fping(targets):
|
def run_fping(targets):
|
||||||
targets = expand_subnets(targets)
|
targets = expand_subnets(targets)
|
||||||
|
|
||||||
if not targets:
|
if not targets:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
is_ipv6 = any(':' in t for t in targets)
|
ipv4_targets = [t for t in targets if ':' not in t]
|
||||||
cmd = ["fping", "-a"] + args.split() + targets
|
ipv6_targets = [t for t in targets if ':' in t]
|
||||||
if is_ipv6:
|
|
||||||
cmd.insert(1, "-6") # insert -6 after "fping"
|
|
||||||
|
|
||||||
if interfaces:
|
def run_family(family_targets, ipv6=False):
|
||||||
cmd += ["-I", ",".join(interfaces)]
|
if not family_targets:
|
||||||
|
return []
|
||||||
|
|
||||||
mylog("verbose", [f"[{pluginName}] fping cmd: {' '.join(cmd)}"])
|
interface_list = interfaces if interfaces else [None]
|
||||||
|
|
||||||
try:
|
all_results = []
|
||||||
output = subprocess.check_output(
|
seen_ips = set()
|
||||||
cmd,
|
|
||||||
stderr=subprocess.DEVNULL,
|
|
||||||
timeout=timeout,
|
|
||||||
text=True
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
output = e.output
|
|
||||||
mylog("none", [f"[{pluginName}] fping returned non-zero exit code, reading alive hosts anyway"])
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
mylog("none", [f"[{pluginName}] fping timeout"])
|
|
||||||
return []
|
|
||||||
|
|
||||||
results = []
|
for interface in interface_list:
|
||||||
for line in output.splitlines():
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip unreachable, timed out, or 100% packet loss
|
cmd = ["fping", "-a"]
|
||||||
if "unreachable" in line.lower() or "timed out" in line.lower() or "100% loss" in line.lower():
|
|
||||||
mylog("debug", [f"[{pluginName}] fping skipping {line}"])
|
|
||||||
continue
|
|
||||||
|
|
||||||
match = ip_regex.search(line)
|
if ipv6:
|
||||||
if match:
|
cmd.append("-6")
|
||||||
ip = match.group(0)
|
|
||||||
mylog("debug", [f"[{pluginName}] adding {ip} from {line}"])
|
|
||||||
results.append((ip, line))
|
|
||||||
else:
|
|
||||||
mylog("verbose", [f"[{pluginName}] fping non-parseable {line}"])
|
|
||||||
|
|
||||||
return results
|
cmd += args.split()
|
||||||
|
|
||||||
|
if interface:
|
||||||
|
cmd += ["-I", interface]
|
||||||
|
|
||||||
|
cmd += family_targets
|
||||||
|
|
||||||
|
mylog("verbose", [f"[{pluginName}] fping cmd: {' '.join(cmd)}"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(
|
||||||
|
cmd,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
timeout=timeout,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
output = e.output
|
||||||
|
mylog("none", [
|
||||||
|
f"[{pluginName}] fping returned non-zero exit code "
|
||||||
|
f"on interface {interface}, reading alive hosts anyway"
|
||||||
|
])
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
mylog("none", [
|
||||||
|
f"[{pluginName}] fping timeout on interface {interface}"
|
||||||
|
])
|
||||||
|
continue
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
lower_line = line.lower()
|
||||||
|
|
||||||
|
if "unreachable" in lower_line or "timed out" in lower_line or "100% loss" in lower_line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = ip_regex.search(line)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
ip = match.group(0)
|
||||||
|
|
||||||
|
if ip in seen_ips:
|
||||||
|
continue
|
||||||
|
|
||||||
|
seen_ips.add(ip)
|
||||||
|
all_results.append((ip, line))
|
||||||
|
|
||||||
|
return all_results
|
||||||
|
|
||||||
|
return run_family(ipv4_targets, ipv6=False) + run_family(ipv6_targets, ipv6=True)
|
||||||
|
|
||||||
# Scan subnets
|
# Scan subnets
|
||||||
mylog("verbose", [f"[{pluginName}] run_fping: subnets {subnets}"])
|
mylog("verbose", [f"[{pluginName}] run_fping: subnets {subnets}"])
|
||||||
|
|||||||
99
front/plugins/kea_api/README.md
Executable file
99
front/plugins/kea_api/README.md
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
## Overview
|
||||||
|
|
||||||
|
A plugin allowing for importing devices from the Kea DHCP API.
|
||||||
|
https://www.isc.org/kea/
|
||||||
|
|
||||||
|
And specifically:
|
||||||
|
https://kea.readthedocs.io/en/kea-2.6.3/api.html#lease4-get-all
|
||||||
|
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
To enable the API, first you want to add something like this to your main kea configuration (this is for debian 13):
|
||||||
|
|
||||||
|
```json
|
||||||
|
"control-socket": {
|
||||||
|
"socket-type": "unix",
|
||||||
|
"socket-name": "/run/kea/kea4-ctrl-socket"
|
||||||
|
},
|
||||||
|
|
||||||
|
"hooks-libraries": [
|
||||||
|
{
|
||||||
|
"library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
And you need to install kea-ctrl-agent, with a config that looks something like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Control-agent": {
|
||||||
|
"http-host": "127.0.0.1",
|
||||||
|
"http-port": 8000,
|
||||||
|
|
||||||
|
"authentication": {
|
||||||
|
"type": "basic",
|
||||||
|
"realm": "Kea Control Agent",
|
||||||
|
"directory": "/etc/kea",
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"user": "kea-api",
|
||||||
|
"password-file": "kea-api-password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"control-sockets": {
|
||||||
|
"dhcp4": {
|
||||||
|
"socket-type": "unix",
|
||||||
|
"socket-name": "/run/kea/kea4-ctrl-socket"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loggers": [
|
||||||
|
{
|
||||||
|
"name": "kea-ctrl-agent",
|
||||||
|
"output-options": [
|
||||||
|
{
|
||||||
|
"output": "stdout",
|
||||||
|
"pattern": "%-5p %m\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"severity": "INFO",
|
||||||
|
"debuglevel": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You will need to configure the plugin with the URL to the API, and the username and password configured above (from kea-api-password file in the example)
|
||||||
|
|
||||||
|
|
||||||
|
#### Required Settings
|
||||||
|
|
||||||
|
These settings are required, besides the common device scanner settings:
|
||||||
|
|
||||||
|
- **Kea Control Agent URL** (`KEALSS_URL`): The full URL, including port number, to the Kea API.
|
||||||
|
- Default: `http://127.0.0.1:8000`
|
||||||
|
- This mirrors what you set up in the kea-ctrl-agent configuration.
|
||||||
|
|
||||||
|
- **Basic Auth Username** (`KEALSS_USER`): The user to use for authenticating with the Kea API.
|
||||||
|
- Default: `kea-api`
|
||||||
|
- This mirrors what you set up in the kea-ctrl-agent configuration.
|
||||||
|
|
||||||
|
- **Basic Auth Password** (`KEALSS_PASS`): The password to use for authenticating with the Kea API.
|
||||||
|
- This mirrors what you set up in the kea-ctrl-agent configuration.
|
||||||
|
- When using a password file, it should be the content of the password file.
|
||||||
|
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This was tested on a basic Debian 13 install.
|
||||||
|
- When you install kea-ctrl-agent, it should ask you about creating a password.
|
||||||
|
- It's possible to run kea-ctrl-agent without password, but it's not recommended and at the moment we don't support that.
|
||||||
|
- I may provide some minimal support, if you ask nicely :)
|
||||||
|
|
||||||
|
- Version: 1.0.0
|
||||||
|
- Author: `void-spark`
|
||||||
|
- Release Date: `11/05/2026`
|
||||||
455
front/plugins/kea_api/config.json
Normal file
455
front/plugins/kea_api/config.json
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
{
|
||||||
|
"code_name": "kea_api",
|
||||||
|
"unique_prefix": "KEALSS",
|
||||||
|
"plugin_type": "device_scanner",
|
||||||
|
"execution_order" : "Layer_3",
|
||||||
|
"enabled": true,
|
||||||
|
"data_source": "script",
|
||||||
|
"data_filters": [
|
||||||
|
{
|
||||||
|
"compare_column": "objectPrimaryId",
|
||||||
|
"compare_operator": "==",
|
||||||
|
"compare_field_id": "txtMacFilter",
|
||||||
|
"compare_js_template": "'{value}'.toString()",
|
||||||
|
"compare_use_quotes": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"show_ui": true,
|
||||||
|
"localized": ["display_name", "description", "icon"],
|
||||||
|
"mapped_to_table": "CurrentScan",
|
||||||
|
"display_name": [{"language_code": "en_us", "string": "Kea DHCP API"}],
|
||||||
|
"icon": [{"language_code": "en_us", "string": "<i class=\"fa-solid fa-hourglass-half\"></i>"}],
|
||||||
|
"description": [{"language_code": "en_us", "string": "Imports leases via Kea Control Agent REST API"}],
|
||||||
|
"database_column_definitions": [
|
||||||
|
{
|
||||||
|
"column": "index",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "none",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Index"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "objectPrimaryId",
|
||||||
|
"mapped_to_column": "scanMac",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "device_mac",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "MAC address"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "objectSecondaryId",
|
||||||
|
"mapped_to_column": "scanLastIP",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "device_ip",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "IP" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "dateTimeCreated",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Created"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "dateTimeChanged",
|
||||||
|
"mapped_to_column": "scanLastConnection",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Changed"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "watchedValue1",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Is active"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "watchedValue2",
|
||||||
|
"mapped_to_column": "scanName",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Hostname"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "watchedValue4",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": true,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "State"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "userData",
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "textbox_save",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Comments"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "Dummy",
|
||||||
|
"mapped_to_column": "scanSourcePlugin",
|
||||||
|
"mapped_to_column_data": {
|
||||||
|
"value": "KEALSS"
|
||||||
|
},
|
||||||
|
"css_classes": "col-sm-2",
|
||||||
|
"show": false,
|
||||||
|
"type": "label",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Scan method"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"column": "status",
|
||||||
|
"css_classes": "col-sm-1",
|
||||||
|
"show": true,
|
||||||
|
"type": "replace",
|
||||||
|
"default_value": "",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"equals": "watched-not-changed",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "watched-changed",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "new",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"equals": "missing-in-last-scan",
|
||||||
|
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"localized": ["name"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Status"}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"function": "RUN",
|
||||||
|
"events": ["run"],
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [{"elementType": "select", "elementOptions": [], "transformers": []}]
|
||||||
|
},
|
||||||
|
"default_value": "disabled",
|
||||||
|
"options": [
|
||||||
|
"disabled",
|
||||||
|
"once",
|
||||||
|
"schedule",
|
||||||
|
"always_after_scan",
|
||||||
|
"on_new_device"
|
||||||
|
],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "When to run"}],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Enable import of devices from <code>Kea API</code>. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) or after you update your settings. ⚠ Use the same schedule if you have multiple <i class=\"fa-solid fa-magnifying-glass-plus\"></i> Device scanners enabled."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "CMD",
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{ "elementType": "input", "elementOptions": [], "transformers": [] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": "python3 /app/front/plugins/kea_api/script.py",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "Command"}],
|
||||||
|
"description": [{"language_code": "en_us", "string": "Command to run"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "URL",
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "API URL"}],
|
||||||
|
"description": [{"language_code": "en_us", "string": "Kea Control Agent URL"}],
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": "http://127.0.0.1:8000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "USER",
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "API User"}],
|
||||||
|
"description": [{"language_code": "en_us", "string": "Basic Auth Username"}],
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": "kea-api"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "PASS",
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [{"language_code": "en_us", "string": "API Password"}],
|
||||||
|
"description": [{"language_code": "en_us", "string": "Basic Auth Password"}],
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [{"type": "password"}],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "RUN_SCHD",
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "span",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"cssClasses": "input-group-addon validityCheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"getStringKey": "Gen_ValidIcon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"focusout": "validateRegex(this)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": "0 2 * * *",
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Schedule"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Only enabled if you select <code>schedule</code> in the <a href=\"#KEALSS_RUN\"><code>KEALSS_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes. <br/> It's recommended to use the same schedule interval for all plugins responsible for discovering new devices."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "RUN_TIMEOUT",
|
||||||
|
"type": {
|
||||||
|
"dataType": "integer",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [{ "type": "number" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": 10,
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Run timeout"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "SET_ALWAYS",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true", "orderable": "true"}],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": ["devMac", "devLastIP"],
|
||||||
|
"options": [
|
||||||
|
"devMac",
|
||||||
|
"devLastIP"
|
||||||
|
],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Set always columns"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (<code>Source = USER</code> or <code>Source = LOCKED</code>)."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "SET_EMPTY",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true", "orderable": "true" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": [],
|
||||||
|
"options": [
|
||||||
|
"devMac",
|
||||||
|
"devLastIP",
|
||||||
|
"devName",
|
||||||
|
"devSourcePlugin"
|
||||||
|
],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Set empty columns"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "These columns are only overwritten if they are empty (<code>NULL</code> / empty string) or if their Source is set to <code>NEWDEV</code>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "WATCH",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true", "orderable": "true"}],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": ["watchedValue1", "watchedValue4"],
|
||||||
|
"options": [
|
||||||
|
"watchedValue1",
|
||||||
|
"watchedValue2",
|
||||||
|
"watchedValue4"
|
||||||
|
],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Watched"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>watchedValue1</code> is Active </li><li><code>watchedValue2</code> is Hostname </li><li><code>watchedValue4</code> is State</li></ul>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "REPORT_ON",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true", "orderable": "true"}],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": ["new", "watched-changed"],
|
||||||
|
"options": [
|
||||||
|
"new",
|
||||||
|
"watched-changed",
|
||||||
|
"watched-not-changed",
|
||||||
|
"missing-in-last-scan"
|
||||||
|
],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Report on"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>watchedValueN</code> columns changed."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
75
front/plugins/kea_api/script.py
Normal file
75
front/plugins/kea_api/script.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../server'))
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../plugins'))
|
||||||
|
|
||||||
|
from plugin_helper import Plugin_Objects, mylog, handleEmpty, is_mac
|
||||||
|
from helper import get_setting_value
|
||||||
|
from const import logPath
|
||||||
|
|
||||||
|
pluginName = 'KEALSS'
|
||||||
|
LOG_PATH = logPath + '/plugins'
|
||||||
|
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
|
||||||
|
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||||
|
|
||||||
|
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
url = get_setting_value(f'{pluginName}_URL')
|
||||||
|
user = get_setting_value(f'{pluginName}_USER')
|
||||||
|
password = get_setting_value(f'{pluginName}_PASS')
|
||||||
|
timeout = get_setting_value(f'{pluginName}_RUN_TIMEOUT')
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] Querying Kea API at {url}'])
|
||||||
|
|
||||||
|
payload = {'command': 'lease4-get-all', 'service': ['dhcp4']}
|
||||||
|
|
||||||
|
response = requests.post(url, json=payload, auth=(user, password), timeout=max(1, timeout - 1))
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for entry in data:
|
||||||
|
text = entry.get('text', '[API provided no text]')
|
||||||
|
# Result: 0 (success), 1 (error), or 3 (empty).
|
||||||
|
if entry['result'] == 0:
|
||||||
|
leases = entry['arguments']['leases']
|
||||||
|
for lease in leases:
|
||||||
|
mac = lease['hw-address']
|
||||||
|
state = lease['state']
|
||||||
|
if is_mac(mac):
|
||||||
|
plugin_objects.add_object(
|
||||||
|
primaryId = mac,
|
||||||
|
secondaryId = lease['ip-address'],
|
||||||
|
# Active or not, similar to watched1 of DHCPLSS plugin
|
||||||
|
watched1 = state == 0,
|
||||||
|
watched2 = lease['hostname'],
|
||||||
|
watched3 = None,
|
||||||
|
# Default (or assigned) (0), declined (1), expired-reclaimed (2), released (3), and registered (4)).
|
||||||
|
watched4 = state,
|
||||||
|
extra = None,
|
||||||
|
foreignKey = mac
|
||||||
|
)
|
||||||
|
count += 1
|
||||||
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] Kea API response: {text}'])
|
||||||
|
mylog('verbose', [f'[{pluginName}] Successfully imported {count} devices reported by Kea API'])
|
||||||
|
elif entry['result'] == 1:
|
||||||
|
mylog('none', [f'[{pluginName}] ⚠ ERROR: Kea API indicated error: {text}'])
|
||||||
|
elif entry['result'] == 3:
|
||||||
|
mylog('verbose', [f'[{pluginName}] Kea API indicates no entries found: {text}'])
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
mylog('none', [f'[{pluginName}] ⚠ ERROR: {str(e)}'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -613,7 +613,8 @@
|
|||||||
"options": [
|
"options": [
|
||||||
"devMac",
|
"devMac",
|
||||||
"devName",
|
"devName",
|
||||||
"devVendor"
|
"devVendor",
|
||||||
|
"devLastIP"
|
||||||
],
|
],
|
||||||
"localized": ["name", "description"],
|
"localized": ["name", "description"],
|
||||||
"name": [
|
"name": [
|
||||||
@@ -810,7 +811,7 @@
|
|||||||
"column": "Dummy",
|
"column": "Dummy",
|
||||||
"mapped_to_column": "scanSourcePlugin",
|
"mapped_to_column": "scanSourcePlugin",
|
||||||
"mapped_to_column_data": {
|
"mapped_to_column_data": {
|
||||||
"value": "sync"
|
"value": "SYNC"
|
||||||
},
|
},
|
||||||
"css_classes": "col-sm-2",
|
"css_classes": "col-sm-2",
|
||||||
"show": false,
|
"show": false,
|
||||||
|
|||||||
@@ -185,9 +185,6 @@ def is_authorized():
|
|||||||
return is_authorized_result
|
return is_authorized_result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/mcp/sse', methods=['GET', 'POST', 'OPTIONS'])
|
@app.route('/mcp/sse', methods=['GET', 'POST', 'OPTIONS'])
|
||||||
def api_mcp_sse():
|
def api_mcp_sse():
|
||||||
if not is_authorized():
|
if not is_authorized():
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
from flask import jsonify, request
|
from flask import jsonify, request
|
||||||
from logger import mylog
|
from logger import mylog, Logger
|
||||||
from helper import get_setting_value
|
from helper import get_setting_value
|
||||||
from utils.datetime_utils import timeNowUTC
|
from utils.datetime_utils import timeNowUTC
|
||||||
from messaging.in_app import write_notification
|
from messaging.in_app import write_notification
|
||||||
|
|
||||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||||
|
|
||||||
|
# Make sure log level is initialized correctly
|
||||||
|
lggr = Logger(get_setting_value('LOG_LEVEL'))
|
||||||
|
|
||||||
def handle_sync_get():
|
def handle_sync_get():
|
||||||
"""Handle GET requests for SYNC (NODE → HUB)."""
|
"""Handle GET requests for SYNC (NODE → HUB)."""
|
||||||
@@ -28,7 +30,11 @@ def handle_sync_get():
|
|||||||
|
|
||||||
response_data = base64.b64encode(raw_data).decode("utf-8")
|
response_data = base64.b64encode(raw_data).decode("utf-8")
|
||||||
|
|
||||||
write_notification("[Plugin: SYNC] Data sent", "info", timeNowUTC())
|
message = "[Plugin: SYNC] Data sent"
|
||||||
|
mylog('verbose', [message])
|
||||||
|
if lggr.isAbove('verbose'):
|
||||||
|
write_notification(message, 'info', timeNowUTC())
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"node_name": get_setting_value("SYNC_node_name"),
|
"node_name": get_setting_value("SYNC_node_name"),
|
||||||
"status": 200,
|
"status": 200,
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ def importConfigs(pm, db, all_plugins):
|
|||||||
# Plugins START
|
# Plugins START
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
# necessary_plugins = ['UI', 'CUSTPROP', 'CLOUD' ,'DBCLNP', 'INTRNT','MAINT','NEWDEV', 'SETPWD', 'SYNC', 'VNDRPDT', 'WORKFLOWS']
|
# necessary_plugins = ['UI', 'CUSTPROP', 'HEARTBEAT' ,'DBCLNP', 'INTRNT','MAINT','NEWDEV', 'SETPWD', 'SYNC', 'VNDRPDT', 'WORKFLOWS']
|
||||||
necessary_plugins = [
|
necessary_plugins = [
|
||||||
"UI",
|
"UI",
|
||||||
"CUSTPROP",
|
"CUSTPROP",
|
||||||
@@ -724,13 +724,13 @@ def importConfigs(pm, db, all_plugins):
|
|||||||
|
|
||||||
write_notification(
|
write_notification(
|
||||||
f"""[Upgrade]: App upgraded from <code>{prev_version}</code> to \
|
f"""[Upgrade]: App upgraded from <code>{prev_version}</code> to \
|
||||||
<code>{new_version}</code> 🚀 Please clear the cache: \
|
<code>{new_version}</code> <i class="fa-solid fa-rocket"></i> Please clear the cache: \
|
||||||
<ol> <li>Click OK below</li> \
|
<ol> <li>Click OK below</li> \
|
||||||
<li>Clear the browser cache (shift + browser refresh button)</li> \
|
<li>Clear the browser cache (shift + browser refresh button)</li> \
|
||||||
<li> Clear app cache with the <i class="fa-solid fa-rotate"></i> (reload) button in the header</li>\
|
<li> Clear app cache with the <i class="fa-solid fa-rotate"></i> (reload) button in the header</li>\
|
||||||
<li>Go to Settings and click Save</li> </ol>\
|
<li>Go to Settings and click Save</li> </ol>\
|
||||||
Check out new features and what has changed in the \
|
Check out new features and what has changed in the \
|
||||||
<a href="https://github.com/netalertx/NetAlertX/releases" target="_blank">📓 release notes</a>.""",
|
<a href="https://github.com/netalertx/NetAlertX/releases" target="_blank"><i class="fa-solid fa-file-pen"></i> release notes</a>.""",
|
||||||
'interrupt',
|
'interrupt',
|
||||||
timeNowUTC()
|
timeNowUTC()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,11 +35,10 @@ from messaging.notification_sections import ( # noqa: E402 [flake8 lint suppres
|
|||||||
)
|
)
|
||||||
import conf # noqa: E402 [flake8 lint suppression]
|
import conf # noqa: E402 [flake8 lint suppression]
|
||||||
|
|
||||||
|
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
# Timezone conversion
|
# Timezone conversion
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
def get_datetime_fields_from_columns(column_names):
|
def get_datetime_fields_from_columns(column_names):
|
||||||
return [
|
return [
|
||||||
col for col in column_names
|
col for col in column_names
|
||||||
@@ -81,6 +80,7 @@ def apply_timezone(data, fields):
|
|||||||
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
value = row.get(field)
|
value = row.get(field)
|
||||||
|
|
||||||
if not value:
|
if not value:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -226,11 +226,24 @@ def get_notifications(db):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
json_obj = db.get_table_as_json(sqlQuery, parameters)
|
json_obj = db.get_table_as_json(sqlQuery, parameters)
|
||||||
|
data = apply_timezone_to_json(json_obj, section)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
mylog("minimal", [f"[Notification] DB error in section {section}: ", e])
|
mylog("none", [f"[Notification] apply_timezone failed for section {section}: ", e])
|
||||||
|
|
||||||
|
# fallback: preserve raw DB payload instead of dropping section
|
||||||
|
try:
|
||||||
|
data = json_obj.json.get("data", [])
|
||||||
|
except Exception:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
final_json[section] = data
|
||||||
|
final_json[f"{section}_meta"] = {
|
||||||
|
"title": SECTION_TITLES.get(section, section),
|
||||||
|
"columnNames": getattr(json_obj, "columnNames", [])
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
final_json[section] = json_obj.json.get("data", [])
|
final_json[section] = data
|
||||||
final_json[f"{section}_meta"] = {
|
final_json[f"{section}_meta"] = {
|
||||||
"title": SECTION_TITLES.get(section, section),
|
"title": SECTION_TITLES.get(section, section),
|
||||||
"columnNames": getattr(json_obj, "columnNames", [])
|
"columnNames": getattr(json_obj, "columnNames", [])
|
||||||
|
|||||||
@@ -206,17 +206,18 @@ def format_date_iso(date_val: str) -> Optional[str]:
|
|||||||
else:
|
else:
|
||||||
dt = date_val
|
dt = date_val
|
||||||
|
|
||||||
# 2. If it has no timezone, assume it's UTC (our DB storage format)
|
# 2. Normalize to UTC first, then convert to target timezone
|
||||||
# then CONVERT to user's configured timezone
|
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
# Mark as UTC first — critical: localize() would label without converting
|
|
||||||
dt = dt.replace(tzinfo=datetime.UTC)
|
dt = dt.replace(tzinfo=datetime.UTC)
|
||||||
# Resolve target timezone; fall back to UTC if conf.tz is missing/invalid
|
else:
|
||||||
try:
|
dt = dt.astimezone(datetime.UTC)
|
||||||
target_tz = conf.tz if isinstance(conf.tz, datetime.tzinfo) else ZoneInfo(conf.tz)
|
|
||||||
except (ZoneInfoNotFoundError, ValueError, TypeError):
|
try:
|
||||||
target_tz = datetime.UTC
|
target_tz = conf.tz if isinstance(conf.tz, datetime.tzinfo) else ZoneInfo(str(conf.tz))
|
||||||
dt = dt.astimezone(target_tz)
|
except Exception:
|
||||||
|
target_tz = datetime.UTC
|
||||||
|
|
||||||
|
dt = dt.astimezone(target_tz)
|
||||||
|
|
||||||
# 3. Return the string. .isoformat() will now include the +11:00 or +10:00
|
# 3. Return the string. .isoformat() will now include the +11:00 or +10:00
|
||||||
return dt.isoformat()
|
return dt.isoformat()
|
||||||
|
|||||||
@@ -14,8 +14,16 @@ class Action:
|
|||||||
def __init__(self, trigger):
|
def __init__(self, trigger):
|
||||||
self.trigger = trigger
|
self.trigger = trigger
|
||||||
|
|
||||||
def execute(self, obj):
|
def get_object(self):
|
||||||
"""Executes the action on the given object."""
|
"""Safely get and normalize the trigger object."""
|
||||||
|
obj = getattr(self.trigger, "object", None)
|
||||||
|
|
||||||
|
if isinstance(obj, sqlite3.Row):
|
||||||
|
obj = dict(obj)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
raise NotImplementedError("Subclasses must implement execute()")
|
raise NotImplementedError("Subclasses must implement execute()")
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +31,7 @@ class UpdateFieldAction(Action):
|
|||||||
"""Action to update a specific field of an object."""
|
"""Action to update a specific field of an object."""
|
||||||
|
|
||||||
def __init__(self, db, field, value, trigger):
|
def __init__(self, db, field, value, trigger):
|
||||||
super().__init__(trigger) # Call the base class constructor
|
super().__init__(trigger)
|
||||||
self.field = field
|
self.field = field
|
||||||
self.value = value
|
self.value = value
|
||||||
self.db = db
|
self.db = db
|
||||||
@@ -31,83 +39,93 @@ class UpdateFieldAction(Action):
|
|||||||
def execute(self):
|
def execute(self):
|
||||||
mylog("verbose", f"[WF] Updating field '{self.field}' to '{self.value}' for event object {self.trigger.object_type}")
|
mylog("verbose", f"[WF] Updating field '{self.field}' to '{self.value}' for event object {self.trigger.object_type}")
|
||||||
|
|
||||||
obj = self.trigger.object
|
obj = self.get_object()
|
||||||
|
|
||||||
# convert to dict for easeir handling
|
if obj is None:
|
||||||
if isinstance(obj, sqlite3.Row):
|
mylog("none", "[WF] Object no longer exists")
|
||||||
obj = dict(obj) # Convert Row object to a standard dictionary
|
return None
|
||||||
|
|
||||||
processed = False
|
|
||||||
|
|
||||||
# currently unused
|
|
||||||
if isinstance(obj, dict) and "objectGuid" in obj:
|
if isinstance(obj, dict) and "objectGuid" in obj:
|
||||||
mylog("debug", f"[WF] Updating Object '{obj}' ")
|
mylog("debug", f"[WF] Updating Object '{obj}'")
|
||||||
plugin_instance = PluginObjectInstance()
|
|
||||||
plugin_instance.updateField(obj["objectGuid"], self.field, self.value)
|
|
||||||
processed = True
|
|
||||||
|
|
||||||
elif isinstance(obj, dict) and "devGUID" in obj:
|
PluginObjectInstance().updateField(
|
||||||
mylog("debug", f"[WF] Updating Device '{obj}' ")
|
obj["objectGuid"],
|
||||||
device_instance = DeviceInstance()
|
self.field,
|
||||||
device_instance.updateField(obj["devGUID"], self.field, self.value)
|
self.value,
|
||||||
processed = True
|
)
|
||||||
|
|
||||||
if not processed:
|
return obj
|
||||||
mylog("none", f"[WF] Could not process action for object: {obj}")
|
|
||||||
|
|
||||||
return obj
|
if isinstance(obj, dict) and "devGUID" in obj:
|
||||||
|
mylog("debug", f"[WF] Updating Device '{obj}'")
|
||||||
|
|
||||||
|
DeviceInstance().updateField(
|
||||||
|
obj["devGUID"],
|
||||||
|
self.field,
|
||||||
|
self.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
mylog("none", f"[WF] Unsupported object format: {obj}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DeleteObjectAction(Action):
|
class DeleteObjectAction(Action):
|
||||||
"""Action to delete an object."""
|
"""Action to delete an object."""
|
||||||
|
|
||||||
def __init__(self, db, trigger):
|
def __init__(self, db, trigger):
|
||||||
super().__init__(trigger) # Call the base class constructor
|
super().__init__(trigger)
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
mylog("verbose", f"[WF] Deleting event object {self.trigger.object_type}")
|
mylog("verbose", f"[WF] Deleting event object {self.trigger.object_type}")
|
||||||
|
|
||||||
obj = self.trigger.object
|
obj = self.get_object()
|
||||||
|
|
||||||
# convert to dict for easeir handling
|
if obj is None:
|
||||||
if isinstance(obj, sqlite3.Row):
|
mylog("none", "[WF] Object no longer exists")
|
||||||
obj = dict(obj) # Convert Row object to a standard dictionary
|
return None
|
||||||
|
|
||||||
processed = False
|
|
||||||
|
|
||||||
# currently unused
|
|
||||||
if isinstance(obj, dict) and "objectGuid" in obj:
|
if isinstance(obj, dict) and "objectGuid" in obj:
|
||||||
mylog("debug", f"[WF] Updating Object '{obj}' ")
|
mylog("debug", f"[WF] Deleting Object '{obj}'")
|
||||||
plugin_instance = PluginObjectInstance()
|
|
||||||
plugin_instance.delete(obj["objectGuid"])
|
|
||||||
processed = True
|
|
||||||
|
|
||||||
elif isinstance(obj, dict) and "devGUID" in obj:
|
PluginObjectInstance().delete(obj["objectGuid"])
|
||||||
mylog("debug", f"[WF] Updating Device '{obj}' ")
|
|
||||||
device_instance = DeviceInstance()
|
|
||||||
device_instance.delete(obj["devGUID"])
|
|
||||||
processed = True
|
|
||||||
|
|
||||||
if not processed:
|
return obj
|
||||||
mylog("none", f"[WF] Could not process action for object: {obj}")
|
|
||||||
|
|
||||||
return obj
|
if isinstance(obj, dict) and "devGUID" in obj:
|
||||||
|
mylog("debug", f"[WF] Deleting Device '{obj}'")
|
||||||
|
|
||||||
|
DeviceInstance().delete(obj["devGUID"])
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
mylog("none", f"[WF] Unsupported object format: {obj}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class RunPluginAction(Action):
|
class RunPluginAction(Action):
|
||||||
"""Action to run a specific plugin."""
|
"""Action to run a specific plugin."""
|
||||||
|
|
||||||
def __init__(self, plugin_name, params, trigger): # Add trigger
|
def __init__(self, plugin_name, params, trigger):
|
||||||
super().__init__(trigger) # Call parent constructor
|
super().__init__(trigger)
|
||||||
self.plugin_name = plugin_name
|
self.plugin_name = plugin_name
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
obj = self.trigger.object
|
obj = self.get_object()
|
||||||
|
|
||||||
|
if obj is None:
|
||||||
|
mylog("none", "[WF] Object no longer exists")
|
||||||
|
return None
|
||||||
|
|
||||||
|
mylog("verbose", f"[WF] Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}")
|
||||||
|
|
||||||
|
# PluginManager.run(self.plugin_name, self.params)
|
||||||
|
|
||||||
mylog("verbose", f"Executing plugin '{self.plugin_name}' with parameters {self.params} for object {obj}")
|
|
||||||
# PluginManager.run(self.plugin_name, self.parameters)
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@@ -115,14 +133,21 @@ class SendNotificationAction(Action):
|
|||||||
"""Action to send a notification."""
|
"""Action to send a notification."""
|
||||||
|
|
||||||
def __init__(self, method, message, trigger):
|
def __init__(self, method, message, trigger):
|
||||||
super().__init__(trigger) # Call parent constructor
|
super().__init__(trigger)
|
||||||
self.method = method # Fix attribute name
|
self.method = method
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
obj = self.trigger.object
|
obj = self.get_object()
|
||||||
mylog("verbose", f"Sending notification via '{self.method}': {self.message} for object {obj}")
|
|
||||||
|
if obj is None:
|
||||||
|
mylog("none", "[WF] Object no longer exists")
|
||||||
|
return None
|
||||||
|
|
||||||
|
mylog("verbose", f"[WF] Sending notification via '{self.method}': {self.message} for object {obj}")
|
||||||
|
|
||||||
# NotificationManager.send(self.method, self.message)
|
# NotificationManager.send(self.method, self.message)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +157,6 @@ class ActionGroup:
|
|||||||
def __init__(self, actions):
|
def __init__(self, actions):
|
||||||
self.actions = actions
|
self.actions = actions
|
||||||
|
|
||||||
def execute(self, obj):
|
def execute(self):
|
||||||
for action in self.actions:
|
for action in self.actions:
|
||||||
action.execute(obj)
|
action.execute()
|
||||||
return obj
|
|
||||||
@@ -48,7 +48,11 @@ class Trigger:
|
|||||||
mylog("debug", [query])
|
mylog("debug", [query])
|
||||||
|
|
||||||
result = db.sql.execute(query).fetchall()
|
result = db.sql.execute(query).fetchall()
|
||||||
self.object = result[0]
|
|
||||||
|
if len(result) > 0:
|
||||||
|
self.object = result[0]
|
||||||
|
else:
|
||||||
|
self.object = None
|
||||||
else:
|
else:
|
||||||
self.object = None
|
self.object = None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user