Compare commits

..

43 Commits

Author SHA1 Message Date
Jokob @NetAlertX
9eaaf50caf Merge pull request #1647 from npt-1707/fix_CVE-2022-24785
Fix potentially vulnerable cloned function
2026-05-18 12:18:45 +10:00
Jokob @NetAlertX
f0684c66c2 Merge pull request #1646 from npt-1707/fix_CVE-2023-22467
Fix potentially vulnerable cloned function
2026-05-18 12:16:43 +10:00
Jokob @NetAlertX
328e591fcd Merge pull request #1645 from npt-1707/fix_CVE-2016-10735
Fix potentially vulnerable cloned function
2026-05-18 12:15:14 +10:00
npt-1707
80c8a66396 front/lib/datatables/datatables.js: Fix XSS in Alert, Carousel, Collapse, Dropdown and Modal 2026-05-18 06:19:59 +08:00
npt-1707
0517da2405 front/lib/moment/moment.js: fix redos using local backtracking regex 2026-05-18 06:17:10 +08:00
npt-1707
35dc9f9fa0 front/lib/moment/moment.js: Avoid loading path-looking locales from fs 2026-05-18 06:15:43 +08:00
jokob-sk
af14dea40c Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-16 14:21:50 +10:00
jokob-sk
292223e062 BE+FE: timestamps for sessions and emails corrected #1639 2026-05-16 14:21:39 +10:00
jokob-sk
47a559197f Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-16 14:14:09 +10:00
jokob-sk
bf4e0b4a7c BE+FE: timestamps for sessions and emails corrected #1639 2026-05-16 14:13:59 +10:00
jokob-sk
92adaddabb Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-16 10:07:49 +10:00
jokob-sk
7725697b87 BE: ICMP fping with multiple interfaces #1642 2026-05-16 10:07:37 +10:00
jokob-sk
09744f3acd Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-16 09:42:55 +10:00
jokob-sk
f6b34845e0 BE: ICMP fping with multiple interfaces #1642 2026-05-16 09:42:44 +10:00
Safeguard
198ca5d410 Translated using Weblate (Russian)
Currently translated at 100.0% (809 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2026-05-15 12:20:33 +02:00
Jokob @NetAlertX
4761a688fc Merge pull request #1643 from netalertx/main
Translated using Weblate (Portuguese (Portugal))
2026-05-15 06:40:15 +10:00
jokob-sk
6e149f8ad3 Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-15 06:37:58 +10:00
jokob-sk
4cac81b5ec PLG: ICMP fping fix for multiple interfaces #1642 2026-05-15 06:37:47 +10:00
António Oliveira
03938d8c28 Translated using Weblate (Portuguese (Portugal))
Currently translated at 100.0% (809 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_PT/
2026-05-14 16:11:33 +00:00
jokob-sk
0127194816 Merge branch 'next_release' of github.com:netalertx/NetAlertX into next_release 2026-05-14 20:36:43 +10:00
jokob-sk
13f8858319 BE: Less verbose SYNC plugin #1164 2026-05-14 20:36:23 +10:00
jokob-sk
f11a63bb7f BE: DIGSCAN could not be disbaled due to default RUN set in config.json #1631 2026-05-13 08:44:29 +10:00
Jokob @NetAlertX
556d104bbb Merge pull request #1638 from netalertx/main
sync
2026-05-12 21:39:10 +00:00
Jokob @NetAlertX
eafbcd52f8 Merge pull request #1636 from void-spark/main
First attempt at kea dhcp support
2026-05-12 21:22:28 +00:00
anton garcias
2fac875792 Translated using Weblate (Catalan)
Currently translated at 99.7% (807 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2026-05-12 18:11:32 +02:00
void-spark
6d661dd12c I did add a ';' from habit, removed again, must please the bunny :) 2026-05-11 22:29:37 +02:00
void-spark
39068e9824 And 'notes' to README.md 2026-05-11 22:21:10 +02:00
void-spark
957c779cb5 Add KEALSS to plugin list 2026-05-11 22:16:51 +02:00
void-spark
76612e5d0e Change default timeout to 10 seconds,
match request timeout to script timeout -1 second.
Always use at least 1 sec. timeout for request.
2026-05-11 22:09:07 +02:00
void-spark
7273899e3e Use single quote consistently
Use 'l' instead of 'lease' (I see you little rabbit)
Use [] over get in most places, if expected fields are missing an error is an acceptable outcome.
Include 'text' field in logging.
Add logging for result '3'
2026-05-11 22:03:29 +02:00
void-spark
608686e4bd Add list with settings to readme 2026-05-11 21:51:32 +02:00
void-spark
b290d3c3d2 First attempt at kea dhcp support 2026-05-10 18:15:05 +02:00
Jokob @NetAlertX
781ae2f91c Merge pull request #1635 from Neutronlul/patch-1
Fix typo in docker installation documentation
2026-05-09 23:57:06 +00:00
Jokob @NetAlertX
f428f45ad2 Update README to include Domotz as a network monitoring option and refine device management logic in deviceDetails.php 2026-05-09 23:53:06 +00:00
Tanner Snow
b7ebc8206f Fix typo in docker installation documentation 2026-05-09 16:47:32 -07:00
Jokob @NetAlertX
9575692a39 Fix unsupported object format handling in UpdateFieldAction and DeleteObjectAction 2026-05-09 23:39:47 +00:00
Jokob @NetAlertX
1def218db5 Update README and enhance device management logic
- Updated Trendshift repository link in README.md.
- Improved error handling and user feedback in deviceDetails.php:
  - Added warnings for device not found scenarios.
  - Refactored device name and owner retrieval logic with caching and REST API fallback.
2026-05-09 23:35:21 +00:00
Jokob @NetAlertX
300820e6bd Merge pull request #1633 from netalertx/main
sync
2026-05-09 22:48:12 +00:00
jokob-sk
8f7f7eaed7 BE: Trigger failing if object non-existent + emoji removal in upgrade message
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
2026-05-10 08:45:52 +10:00
António Oliveira
190262a730 Translated using Weblate (Portuguese (Portugal))
Currently translated at 96.2% (779 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_PT/
2026-05-08 15:11:37 +02:00
大王叫我来巡山
5ba202c6a1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 97.2% (787 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/zh_Hans/
2026-05-08 15:11:32 +02:00
IntEx
e262f915e2 Translated using Weblate (Czech)
Currently translated at 57.8% (468 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/cs/
2026-05-07 03:11:35 +02:00
António Oliveira
b8c4e62ad5 Translated using Weblate (Portuguese (Portugal))
Currently translated at 85.2% (690 of 809 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_PT/
2026-05-07 03:11:33 +02:00
26 changed files with 1140 additions and 387 deletions

View File

@@ -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

View File

@@ -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'

View File

@@ -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

View File

@@ -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 | | |

View File

@@ -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);
}
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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()

View File

@@ -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) {

View File

@@ -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ó."
} }

View File

@@ -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": "",

View File

@@ -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."
} }

View File

@@ -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": "Действие не зарегистрировано:· ",

View File

@@ -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": "在测试设置之前,请先保存更改。"
} }

View File

@@ -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",

View File

@@ -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
View 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`

View 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."
}
]
}
]
}

View 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()

View File

@@ -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,

View File

@@ -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():

View File

@@ -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,

View File

@@ -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()
) )

View File

@@ -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", [])

View File

@@ -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()

View File

@@ -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

View File

@@ -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