Compare commits

..

13 Commits

Author SHA1 Message Date
Jokob @NetAlertX
cb8af32553 Merge pull request #970 from labmonkey/asuswrt-import
Some checks are pending
docker / docker_dev (push) Waiting to run
[ASUSWRT] Added router port to configurtation
2025-01-21 06:43:42 +11:00
jokob-sk
d469a9ded4 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-20 23:42:48 +11:00
jokob-sk
c8a40920b4 cleanup, faster devices screen update #967 #923 2025-01-20 23:42:24 +11:00
Максим Горпиніч
b0cd9acb79 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (752 of 752 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-01-20 08:35:13 +01:00
Anonymous
6129f31a24 Translated using Weblate (Polish)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 88.6% (667 of 752 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pl/
2025-01-20 06:17:17 +01:00
Massimo Pissarello
3eb8f39b5c Translated using Weblate (Italian)
Currently translated at 100.0% (752 of 752 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-20 06:17:17 +01:00
jokob-sk
5b1002620b cleanup, css, rebase to alpine 3.21 #968 2025-01-20 14:55:26 +11:00
Pawel Derehajlo
e50d757f57 [ASUSWRT] Added router port to configurtation 2025-01-20 02:07:47 +01:00
Jokob @NetAlertX
5110a3c2f3 Merge pull request #966 from vladaurosh/main
Using busybox crond instead of dcron, starting it with s6
2025-01-20 11:47:14 +11:00
root
abf7be5958 adding new line for pem file 2025-01-19 22:23:52 +00:00
root
82708bd5df Fixing path to cert file 2025-01-19 22:19:00 +00:00
root
b5dce3f6aa Fixing crond issues #945 and #965 2025-01-19 22:11:32 +00:00
jokob-sk
5562ae7add docs + wf
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-19 23:19:26 +11:00
66 changed files with 428 additions and 543 deletions

View File

@@ -12,13 +12,13 @@ jobs:
# Post to Twitter
- name: Post to Twitter
uses: gr2m/twitter-together@v2
uses: twitter-together/action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
TWITTER_API_SECRET_KEY: ${{ secrets.TWITTER_API_SECRET_KEY }}
with:
tweet: |
🎉 New release: **${{ github.event.release.name }}** is live! 🚀

View File

@@ -1,11 +1,11 @@
FROM alpine:3.20 AS builder
FROM alpine:3.21 AS builder
ARG INSTALL_DIR=/app
ENV PYTHONUNBUFFERED=1
# Install build dependencies
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev git\
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev git \
&& python -m venv /opt/venv
# Enable venv
@@ -14,29 +14,16 @@ ENV PATH="/opt/venv/bin:$PATH"
COPY . ${INSTALL_DIR}/
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \
RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
# Append Iliadbox certificate to aiofreepybox
RUN printf "\n-----BEGIN CERTIFICATE-----\n\
MIICOjCCAcCgAwIBAgIUI0Tu7zsrBJACQIZgLMJobtbdNn4wCgYIKoZIzj0EAwIw\n\
TDELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MQ4wDAYDVQQKDAVJbGlhZDEd\n\
MBsGA1UEAwwUSWxpYWRib3ggRUNDIFJvb3QgQ0EwHhcNMjAxMTI3MDkzODEzWhcN\n\
NDAxMTIyMDkzODEzWjBMMQswCQYDVQQGEwJJVDEOMAwGA1UECAwFSXRhbHkxDjAM\n\
BgNVBAoMBUlsaWFkMR0wGwYDVQQDDBRJbGlhZGJveCBFQ0MgUm9vdCBDQTB2MBAG\n\
ByqGSM49AgEGBSuBBAAiA2IABMryJyb2loHNAioY8IztN5MI3UgbVHVP/vZwcnre\n\
ZvJOyDvE4HJgIti5qmfswlnMzpNbwf/MkT+7HAU8jJoTorRm1wtAnQ9cWD3Ebv79\n\
RPwtjjy3Bza3SgdVxmd6fWPUKaNjMGEwHQYDVR0OBBYEFDUij/4lpoJ+kOXRyrcM\n\
jf2RPzOqMB8GA1UdIwQYMBaAFDUij/4lpoJ+kOXRyrcMjf2RPzOqMA8GA1UdEwEB\n\
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQC6eUV1\n\
pFh4UpJOTc1JToztN4ttnQR6rIzxMZ6mNCe+nhjkohWp24pr7BpUYSbEizYCMAQ6\n\
LCiBKV2j7QQGy7N1aBmdur17ZepYzR1YV0eI+Kd978aZggsmhjXENQYVTmm/XA==\n\
-----END CERTIFICATE-----\n" >> /opt/venv/lib/python3.12/site-packages/aiofreepybox/freebox_certificates.pem
RUN cat ${INSTALL_DIR}/install/freebox_certificate.pem >> /opt/venv/lib/python3.12/site-packages/aiofreepybox/freebox_certificates.pem
# second stage
FROM alpine:3.20 AS runner
FROM alpine:3.21 AS runner
ARG INSTALL_DIR=/app
@@ -55,10 +42,9 @@ ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
RUN apk update --no-cache \
&& apk add --no-cache bash zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan avahi avahi-tools openrc dbus net-tools net-snmp-tools bind-tools awake ca-certificates \
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan avahi avahi-tools openrc dbus net-tools net-snmp-tools bind-tools awake ca-certificates \
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
&& apk add --no-cache python3 nginx \
&& apk add --no-cache dcron \
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
&& bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}" \
&& rm -f /etc/nginx/http.d/default.conf
@@ -66,7 +52,7 @@ RUN apk update --no-cache \
COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/ ${INSTALL_DIR}/
# Add crontab file
COPY install/crontab /etc/crontabs/root
COPY --chmod=600 --chown=root:root install/crontab /etc/crontabs/root
# Start all required services
RUN ${INSTALL_DIR}/dockerfiles/start.sh

View File

@@ -43,7 +43,7 @@ RUN phpenmod -v 8.2 sqlite3
RUN apt-get install -y python3-venv
RUN python3 -m venv myenv
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros "
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros "
# Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt

View File

@@ -5,8 +5,9 @@ LOG_FILE="${INSTALL_DIR}/log/execution_queue.log"
# Check if there are any entries with cron_restart_backend
if grep -q "cron_restart_backend" "$LOG_FILE"; then
# Kill all python processes (restart handled by s6 overlay)
pkill -f "python " && echo 'done'
# Restart python application using s6
s6-svc -r /var/run/s6-rc/servicedirs/netalertx
echo 'done'
# Remove all lines containing cron_restart_backend from the log file
sed -i '/cron_restart_backend/d' "$LOG_FILE"

View File

@@ -62,7 +62,6 @@ services:
- ${DEV_LOCATION}/front/workflows.php:/app/front/workflows.php
- ${DEV_LOCATION}/front/appEventsCore.php:/app/front/appEventsCore.php
- ${DEV_LOCATION}/front/multiEditCore.php:/app/front/multiEditCore.php
- ${DEV_LOCATION}/front/donations.php:/app/front/donations.php
- ${DEV_LOCATION}/front/plugins:/app/front/plugins
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
# ---------------------------------------------------------------------------

View File

@@ -125,10 +125,6 @@ if [ ! -f "${INSTALL_DIR}/front/buildtimestamp.txt" ]; then
chown nginx:www-data "${INSTALL_DIR}/front/buildtimestamp.txt"
fi
# Start crond service in the background
echo "[INSTALL] Starting crond service..."
crond -f -d 8 > /dev/null 2>&1 &
echo -e "
[ENV] PATH is ${PATH}
[ENV] PORT is ${PORT}

View File

@@ -20,7 +20,14 @@ echo "longrun" > /etc/s6-overlay/s6-rc.d/php-fpm/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/$APP_NAME/type
echo -e "${INSTALL_DIR}/dockerfiles/init.sh" > /etc/s6-overlay/s6-rc.d/SetupOneshot/up
echo -e "#!/bin/execlineb -P\n/usr/sbin/crond -f -d 8" > /etc/s6-overlay/s6-rc.d/crond/run
echo -e '#!/bin/execlineb -P
if { echo
"
[INSTALL] Starting crond service...
" }' > /etc/s6-overlay/s6-rc.d/crond/run
echo -e "/usr/sbin/crond -f" >> /etc/s6-overlay/s6-rc.d/crond/run
echo -e "#!/bin/execlineb -P\n/usr/sbin/php-fpm83 -F" > /etc/s6-overlay/s6-rc.d/php-fpm/run
echo -e '#!/bin/execlineb -P\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
echo -e '#!/bin/execlineb -P
@@ -39,4 +46,4 @@ touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm
touch /etc/s6-overlay/s6-rc.d/$APP_NAME/dependencies.d/nginx
# this removes the current file
# rm -f $0
rm -f $0

View File

@@ -1331,8 +1331,8 @@ input[readonly] {
top: -6px;
position: absolute;
z-index: 1;
left: 0px;
font-size: large;
left: 4px;
font-size: smaller;
}
.drag
@@ -1397,6 +1397,7 @@ input[readonly] {
{
display: none;
padding-top: 2em;
cursor: default;
}
/* ----------------------------------------------------------------- */
@@ -1618,6 +1619,20 @@ input[readonly] {
}
}
/* ----------------------------------------------------------------- */
/* EVENTS page */
/* ----------------------------------------------------------------- */
.eventsPeriodSelectWrap{
display: inline;
float: right;
}
.eventsPage #tableEventsTitle
{
float: left ;
}
/* ----------------------------------------------------------------- */
/* PLUGINS page */
/* ----------------------------------------------------------------- */
@@ -1800,10 +1815,7 @@ input[readonly] {
----------------------------------------------------------------------------- */
#multiEditPlc
{
position: fixed;
bottom: 50px;
right: 0px;
z-index: 10;
padding-right: 10px;
}

View File

@@ -28,14 +28,6 @@
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-laptop"></i>
<?= lang('Device_Title');?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
@@ -78,6 +70,9 @@
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
</div>
<div class="dummyDevice col-md-3 ">
<span id="multiEditPlc">
<!-- multi edit button placeholder -->
</span>
<span>
<a href="deviceDetails.php?mac=new"><i title="<?= lang('Gen_create_new_device');?>" class="fa fa-square-plus"></i> <?= lang('Gen_create_new_device');?></a>
</span>
@@ -106,7 +101,7 @@
<!-- ----------------------------------------------------------------------- -->
</section>
<!-- /.content -->
<div id="multiEditPlc" class="col-md-2"></div>
</div>
<!-- /.content-wrapper -->
@@ -749,9 +744,9 @@ function initializeDatatable (status) {
// add multi-edit button
$('#multiEditPlc').append(
`<button type="submit" id="multiEdit" class="btn btn-primary" style="display:none" onclick="multiEditDevices();">
<i class="fa fa-pencil pointer" ></i> ${getString("Device_MultiEdit")}
</button>`)
`<span type="submit" id="multiEdit" class="pointer " style="display:none" onclick="multiEditDevices();">
<a href="#"><i class="fa fa-pencil " ></i> ${getString("Device_MultiEdit")} </a>
</span>`)
// Event listener for row selection in DataTable
$('#tableDevices').on('click', 'tr', function (e) {

View File

@@ -1,70 +0,0 @@
<?php
require 'php/templates/header.php';
?>
<div id="donationsPage" class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-heart"></i>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content donations">
<div id="donationsText" class="box box-solid"></div>
<div class="content-header">
<h3 class="box-title " id="donationsPlatforms"></h3>
</div>
<div class="box box-solid">
<div class="box-body">
<div class="col-sm-2">
<a target="_blank" href="https://github.com/sponsors/jokob-sk">
<img alt="Sponsor Me on GitHub" src="https://i.imgur.com/X6p5ACK.png" width="150px">
</a>
</div>
<div class="col-sm-2">
<a target="_blank" href="https://www.buymeacoffee.com/jokobsk">
<img alt="Buy Me A Coffee" src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" width="117px" height="30px">
</a>
</div>
<div class="col-sm-2">
<a target="_blank" href="https://www.patreon.com/user?u=84385063">
<img alt="Support me on patreon" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" width="117px">
</a>
</div>
</div>
</div>
<div class="content-header">
<h3 class="box-title " id="donationsOthers"></h3>
</div>
<div class="box box-solid">
<div class="box-body">
<div class="col-sm-12">
<ul>
<li>Bitcoin: <code>1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM</code></li>
<li>Ethereum: <code>0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7</code></li>
</ul>
</div>
</div>
</div>
<div>
</section>
</div> <!-- End of class="content-wrapper" -->
<script>
function init()
{
$("#donationsText").html(getString("Donations_Text"))
$("#pageTitle").append(getString("Donations_Title"))
$("#donationsPlatforms").append(getString("Donations_Platforms"))
$("#donationsOthers").append(getString("Donations_Others"))
}
init();
</script>
<?php
require 'php/templates/footer.php';
?>

View File

@@ -1,17 +1,3 @@
<!--
#---------------------------------------------------------------------------------#
# NetAlertX #
# Open Source Network Guard / WIFI & LAN intrusion detector #
# #
# events.php - Front module. Events page #
#---------------------------------------------------------------------------------#
# Puche 2021 pi.alert.application@gmail.com GNU GPLv3 #
# jokob-sk 2022 jokob.sk@gmail.com GNU GPLv3 #
# leiweibau 2022 https://github.com/leiweibau GNU GPLv3 #
# cvc90 2023 https://github.com/cvc90 GNU GPLv3 #
#---------------------------------------------------------------------------------#
-->
<?php
require 'php/templates/header.php';
?>
@@ -19,26 +5,7 @@
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-bolt"></i>
<?= lang('Events_Title');?>
</h1>
<!-- period selector -->
<span class="breadcrumb" style="top: 0px;">
<select class="form-control" id="period" onchange="javascript: periodChanged();">
<option value="1 day"><?= lang('Events_Periodselect_today');?></option>
<option value="7 days"><?= lang('Events_Periodselect_LastWeek');?></option>
<option value="1 month" selected><?= lang('Events_Periodselect_LastMonth');?></option>
<option value="1 year"><?= lang('Events_Periodselect_LastYear');?></option>
<option value="100 years"><?= lang('Events_Periodselect_All');?></option>
</select>
</span>
</section>
<div class="content-wrapper eventsPage">
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
@@ -123,15 +90,31 @@
<!-- datatable ------------------------------------------------------------- -->
<div class="row">
<div class="col-xs-12">
<div id="tableEventsBox" class="box">
<!-- box-header -->
<div class="box-header">
<h3 id="tableEventsTitle" class="box-title text-gray">Events</h3>
<div class="box-header col-xs-12">
<h3 id="tableEventsTitle" class="box-title text-gray col-xs-10">Events</h3>
<div class="eventsPeriodSelectWrap col-xs-2">
<select class="form-control" id="period" onchange="javascript: periodChanged();">
<option value="1 day"><?= lang('Events_Periodselect_today');?></option>
<option value="7 days"><?= lang('Events_Periodselect_LastWeek');?></option>
<option value="1 month" selected><?= lang('Events_Periodselect_LastMonth');?></option>
<option value="1 year"><?= lang('Events_Periodselect_LastYear');?></option>
<option value="100 years"><?= lang('Events_Periodselect_All');?></option>
</select>
</div>
</div>
<!-- table -->
<div class="box-body table-responsive">
<table id="tableEvents" class="table table-bordered table-hover table-striped ">
<thead>
<tr>

View File

@@ -1,18 +1,9 @@
<?php
require 'php/templates/header.php';
require 'php/templates/notification.php';
?>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-question"></i>
<?= lang('HelpFAQ_Title');?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
<h4>

View File

@@ -1,47 +1,16 @@
<?php
#---------------------------------------------------------------------------------#
# NetAlertX #
# Open Source Network Guard / WIFI & LAN intrusion detector #
# #
# maintenance.php - Front module. Server side. Maintenance #
#---------------------------------------------------------------------------------#
# Puche 2021 pi.alert.application@gmail.com GNU GPLv3 #
# jokob-sk 2022 jokob.sk@gmail.com GNU GPLv3 #
# leiweibau 2022 https://github.com/leiweibau GNU GPLv3 #
# cvc90 2023 https://github.com/cvc90 GNU GPLv3 #
#---------------------------------------------------------------------------------#
//------------------------------------------------------------------------------
?>
<?php
require 'php/templates/header.php';
require 'php/templates/notification.php';
?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper" id="maintenancePage">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-wrench"></i>
<?= lang('Maintenance_Title');?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
<?php
<?php
// Size and last mod of DB ------------------------------------------------------
@@ -91,10 +60,11 @@ $db->close();
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_version');?>
<a href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/VERSIONS.md" target="_blank"> <span><i class="fa fa-circle-question"></i></a><span>
</div>
<div class="db_info_table_cell">
<div class="version" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>"><?php echo '<span id="new-version-text" class="myhidden">' .lang('Maintenance_new_version').'</span>'.'<span id="current-version-text" class="myhidden">' .lang('Maintenance_current_version').'</span>';?></div>
<div class="version" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">
<?php echo '<span id="new-version-text" class="myhidden"><i class="fa-solid fa-rocket fa-beat"></i> ' .lang('Maintenance_new_version').'</span>'.'<span id="current-version-text" class="myhidden">' .lang('Maintenance_current_version').'</span>';?>
</div>
</div>
</div>
<div class="db_info_table_row">

View File

@@ -1,6 +1,7 @@
<?php
require 'php/templates/header.php';
require 'php/templates/notification.php';
// online / offline badges HTML snippets
define('badge_online', '<div class="badge bg-green text-white" style="width: 60px;">Online</div>');
@@ -15,21 +16,13 @@
// show spinning icon
showSpinner()
</script>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-network-wired"></i> <?= lang('Network_Title');?>
<span class="helpIconSmallTopRight"> <a target="_blank" href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md"><i class="fa fa-circle-question"></i></a><span>
</h1>
</section>
<span class="networkHelpIcon"> <a target="_blank" href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md"><i class="fa fa-circle-question"></i></a></span>
<div id="networkTree" class="drag"></div>

View File

@@ -356,7 +356,10 @@
<!-- Maintenance menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active menu-open'; } ?>">
<a href="#" onclick="openUrl(['./maintenance.php'])">
<div class="info-icon-nav myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
<!-- NEW version available -->
<div class="info-icon-nav myhidden" id="version" title="<?= lang('new_version_available');?>" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">
<i class="fa-solid fa-rocket fa-beat"></i>
</div>
<i class="fa fa-fw fa-wrench"></i> <span><?= lang('Navigation_Maintenance');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
@@ -430,17 +433,14 @@
</li>
<!-- About menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'active menu-open'; } ?>">
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('help_faq.php', 'systeminfo.php' ) ) ){ echo 'active menu-open'; } ?>">
<a href="#">
<i class="fa fa-fw fa-info"></i> <span><?= lang('Navigation_About');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="donations.php"> <?= lang("Navigation_Donations");?> </a>
</li>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('help_faq.php', 'systeminfo.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="help_faq.php"> <?= lang("Navigation_HelpFAQ");?> </a>
</li>

View File

@@ -716,6 +716,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Tria idioma",
"Maintenance_lang_selector_lable": "Selecció d'idioma",
"Maintenance_lang_selector_text": "El canvi té lloc en el cantó del client, així que afecta només el navegador actual.",
"Maintenance_new_version": "🆕 Hi ha una nova versió. Comprova <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">release notes</a>.",
"Maintenance_new_version": "Hi ha una nova versió. Comprova <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">release notes</a>.",
"Maintenance_themeselector_apply": "Aplica",
"Maintenance_themeselector_empty": "Tria una Skin",
"Maintenance_themeselector_lable": "Selecciona una Skin",
@@ -716,6 +716,7 @@
"general_event_title": "Execució d'un esdeveniment ad-hoc",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Navegació a la pàgina de la Xarxa del node donat",
"new_version_available": "",
"report_guid": "Notificació guid:",
"report_guid_missing": "No s'ha trobat la notificació enllaçada. Hi ha un petit retard entre les notificacions enviades recentment i que estiguin disponibles. Refresqui la pàgina i la memòria cau d'aquí uns segons. També és possible que la notificació seleccionada s'hagi esborrat durant el manteniment tal com s'especifica a la configuració <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>L'última notificació es mostra en el seu lloc. La notificació perduda té el següent GUID:",
"report_select_format": "Seleccioneu Format:",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "Actualitza el valor sota. Sigues curós de seguir el format anterior. <b>No hi ha validació.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Deseu els canvis primer abans de comprovar la configuració."
}
}

View File

@@ -716,6 +716,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",

View File

@@ -515,7 +515,7 @@
"Maintenance_lang_selector_empty": "Sprache wählen",
"Maintenance_lang_selector_lable": "Sprachauswahl",
"Maintenance_lang_selector_text": "Die Änderung findet serverseitig statt, betrifft also alle verwendeten Geräte.",
"Maintenance_new_version": "🆕 Eine neue Version ist vefügbar. Sieh dir die <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">Versionshinweise</a> an.",
"Maintenance_new_version": "Eine neue Version ist vefügbar. Sieh dir die <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">Versionshinweise</a> an.",
"Maintenance_themeselector_apply": "Übernehmen",
"Maintenance_themeselector_empty": "Skin wählen",
"Maintenance_themeselector_lable": "Skin Auswahl",
@@ -797,6 +797,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "Format auswählen:",
@@ -831,4 +832,4 @@
"settings_update_item_warning": "",
"test_event_icon": "",
"test_event_tooltip": "Speichere die Änderungen, bevor Sie die Einstellungen testen."
}
}

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Choose Language",
"Maintenance_lang_selector_lable": "Select Language",
"Maintenance_lang_selector_text": "The change takes place on the client side, so it affects only the current browser.",
"Maintenance_new_version": "🆕 A new version is available. Check out the <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">release notes</a>.",
"Maintenance_new_version": "A new version is available. Check out the <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">release notes</a>.",
"Maintenance_themeselector_apply": "Apply",
"Maintenance_themeselector_empty": "Choose a Skin",
"Maintenance_themeselector_lable": "Select Skin",
@@ -716,6 +716,7 @@
"general_event_title": "Executing an ad-hoc event",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Navigate to the Network page of the given node",
"new_version_available": "A new version is available.",
"report_guid": "Notification guid:",
"report_guid_missing": "Linked notification not found. There is a small delay between recently sent notifications and them being available. Referesh your page and cache after a few seconds. It's also possible the selected notification have been deleted during maintenance as specified in the <code>DBCLNP_NOTIFI_HIST</code> setting. <br/> <br/>The latest notification is displayed instead. The missing notification has the following GUID:",
"report_select_format": "Select Format:",

View File

@@ -513,7 +513,7 @@
"Maintenance_lang_selector_empty": "Elija un idioma",
"Maintenance_lang_selector_lable": "Seleccione su idioma",
"Maintenance_lang_selector_text": "El cambio se produce en el lado del cliente, por lo que sólo afecta al navegador actual.",
"Maintenance_new_version": "🆕 Una nueva versión está disponible. Comprueba las <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">notas de lanzamiento</a>.",
"Maintenance_new_version": "Una nueva versión está disponible. Comprueba las <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">notas de lanzamiento</a>.",
"Maintenance_themeselector_apply": "Aplicar",
"Maintenance_themeselector_empty": "Elige un tema",
"Maintenance_themeselector_lable": "Seleccionar tema",
@@ -795,6 +795,7 @@
"general_event_title": "Ejecutar un evento ad-hoc",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Vaya a la página de Red del nodo indicado",
"new_version_available": "",
"report_guid": "Guía de las notificaciones:",
"report_guid_missing": "No se ha encontrado la notificación vinculada. Hay un pequeño retraso entre las notificaciones enviadas recientemente y su disponibilidad. Actualiza tu página y la caché después de unos segundos. También es posible que la notificación seleccionada se haya eliminado durante el mantenimiento, tal y como se especifica en la configuración <code>de DBCLNP_NOTIFI_HIST</code>. <br/> <br/>En su lugar, se muestra la notificación más reciente. La notificación que falta tiene el siguiente GUID:",
"report_select_format": "Selecciona el formato:",
@@ -829,4 +830,4 @@
"settings_update_item_warning": "Actualice el valor a continuación. Tenga cuidado de seguir el formato anterior. <b>O la validación no se realiza.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Guarda tus cambios antes de probar nuevos ajustes."
}
}

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Choix de la langue",
"Maintenance_lang_selector_lable": "Sélectionner une langue",
"Maintenance_lang_selector_text": "Le changement est effectué côté client, cela ne concerne donc que le navigateur actuel.",
"Maintenance_new_version": "🆕 Une nouvelle version est disponible. Consulter les <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">notes de version</a>.",
"Maintenance_new_version": "Une nouvelle version est disponible. Consulter les <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">notes de version</a>.",
"Maintenance_themeselector_apply": "Appliquer",
"Maintenance_themeselector_empty": "Choisir un thème",
"Maintenance_themeselector_lable": "Sélectionner un thème",
@@ -716,6 +716,7 @@
"general_event_title": "Lancement d'un événement sur mesure",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Aller vers la page Réseau du nœud concerné",
"new_version_available": "",
"report_guid": "GUID de la notification:",
"report_guid_missing": "La notification associée n'a pas été trouvée. Un petit délai existe entre l'envoi d'une notification et sa disponibilité réelle pour affichage. Rafraichissez la page et votre cache après quelques secondes. Il est aussi possible que la notification sélectionnée ait été supprimée durant une opération de maintenance, comme renseigné dans le paramètre <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/> La dernière notification est affichée à sa place. La notification manquante dispose du GUID suivant:",
"report_select_format": "Sélectionner un format:",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "Mettre à jour la valeur ci-dessous. Veillez à bien suivre le même format qu'auparavant. <b>Il n'y a pas de pas de contrôle.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
}
}

3
front/php/templates/language/it_it.json Executable file → Normal file
View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Scegli lingua",
"Maintenance_lang_selector_lable": "Seleziona lingua",
"Maintenance_lang_selector_text": "Questa modifica avviene lato client, quindi influisce solo sul browser attualmente in uso.",
"Maintenance_new_version": "🆕 È disponibile una nuova versione. Controlla le <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">note di rilascio</a>.",
"Maintenance_new_version": "È disponibile una nuova versione. Controlla le <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">note di rilascio</a>.",
"Maintenance_themeselector_apply": "Applica",
"Maintenance_themeselector_empty": "Scegli una skin",
"Maintenance_themeselector_lable": "Seleziona skin",
@@ -716,6 +716,7 @@
"general_event_title": "Esecuzione di un evento ad-hoc",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Passa alla pagina Rete del nodo specificato",
"new_version_available": "È disponibile una nuova versione.",
"report_guid": "GUID notifica:",
"report_guid_missing": "Notifica collegata non trovata. C'è un piccolo ritardo tra la disponibilità delle notifiche inviate di recente e la loro disponibilità. Aggiorna la pagina e la cache dopo alcuni secondi. È anche possibile che la notifica selezionata sia stata eliminata durante la manutenzione come specificato nell'impostazione <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Viene invece visualizzata l'ultima notifica. La notifica mancante ha il seguente GUID:",
"report_select_format": "Seleziona formato:",

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Velg språk",
"Maintenance_lang_selector_lable": "Velg språk",
"Maintenance_lang_selector_text": "Endringen skjer på klientsiden, så den påvirker bare den nåværende nettleseren.",
"Maintenance_new_version": "🆕 En ny versjon er tilgjengelig. Sjekk ut <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">utgivelsesnotater</a>.",
"Maintenance_new_version": "En ny versjon er tilgjengelig. Sjekk ut <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">utgivelsesnotater</a>.",
"Maintenance_themeselector_apply": "Bruk",
"Maintenance_themeselector_empty": "Velg ett skinn",
"Maintenance_themeselector_lable": "Velg skinn",
@@ -716,6 +716,7 @@
"general_event_title": "Utfører en ad-hoc hendelse",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "Notifikasjons GUID:",
"report_guid_missing": "Koblet notifikasjon ikke funnet. Det er en liten forsinkelse mellom nylig sendt notifikasjoner og at de er tilgjengelige. Oppdater siden din og hurtigbufferen etter noen sekunder. Det er også mulig den valgte notifikasjonen er slettet under vedlikehold som spesifisert i <code>DBCLNP_NOTIFI_HIST</code> innstillingen. <br/> <br/> Den siste notifikasjonen vises i stedet. Den manglende notifikasjonen har følgende GUID:",
"report_select_format": "Velg format:",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "Oppdater verdien nedenfor. Pass på å følge forrige format. <b>Validering etterpå utføres ikke.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Lagre endringene først, før du tester innstillingene dine."
}
}

1
front/php/templates/language/pl_pl.json Executable file → Normal file
View File

@@ -716,6 +716,7 @@
"general_event_title": "Wykonywanie wydarzeń ad-hoc",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "Przewodnik powiadomień:",
"report_guid_missing": "Linkowane powiadomienie nie znaleziono. Jest małe opóźnienie między wysłaniem powiadomienia a ich dostępnością. Odśwież stronę i cache za kilka sekund. Możliwe też że zaznaczone powiadomienie zostało usunięte podczas konserwacji tak jak określono w ustawieniu <code>DBCLNP_NOTIFI_HIST</code>. <br/><br/>Zamiast tego wyświetlane jest najnowsze powiadomienie. Brakujące powiadomienie ma następujące GUID:",
"report_select_format": "Wybierz Format:",

View File

@@ -716,6 +716,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "",
"test_event_icon": "",
"test_event_tooltip": ""
}
}

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Выберите язык",
"Maintenance_lang_selector_lable": "Выбрать язык",
"Maintenance_lang_selector_text": "Изменение происходит на стороне клиента, поэтому оно влияет только на текущий браузер.",
"Maintenance_new_version": "🆕 Доступна новая версия. Ознакомьтесь с <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">примечаниями к выпуску</a>.",
"Maintenance_new_version": "Доступна новая версия. Ознакомьтесь с <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">примечаниями к выпуску</a>.",
"Maintenance_themeselector_apply": "Применить",
"Maintenance_themeselector_empty": "Выбрать скин",
"Maintenance_themeselector_lable": "Выбрать Скин",
@@ -716,6 +716,7 @@
"general_event_title": "Выполнение специального события",
"go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Переход на страницу \"Сеть\" данного узла",
"new_version_available": "",
"report_guid": "Идентификатор уведомления:",
"report_guid_missing": "Связанное уведомление не найдено. Между недавно отправленными уведомлениями и их доступностью существует небольшая задержка. Обновите страницу и кэшируйте ее через несколько секунд. Также возможно, что выбранное уведомление было удалено во время обслуживания, как указано в настройке <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Вместо этого отображается последнее уведомление. Отсутствующее уведомление имеет следующий GUID:",
"report_select_format": "Выбрать формат:",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
}
}

View File

@@ -716,6 +716,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "",

3
front/php/templates/language/uk_ua.json Executable file → Normal file
View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Виберіть мову",
"Maintenance_lang_selector_lable": "Виберіть мови",
"Maintenance_lang_selector_text": "Зміна відбувається на стороні клієнта, тому вона впливає лише на поточний браузер.",
"Maintenance_new_version": "🆕 Доступна нова версія. Перегляньте <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">примітки до випуску</a>.",
"Maintenance_new_version": "Доступна нова версія. Перегляньте <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">примітки до випуску</a>.",
"Maintenance_themeselector_apply": "Застосувати",
"Maintenance_themeselector_empty": "Виберіть скін",
"Maintenance_themeselector_lable": "Виберіть Скін",
@@ -716,6 +716,7 @@
"general_event_title": "Виконання спеціальної події",
"go_to_node_event_icon": "fa-квадрат-вгору-вправо",
"go_to_node_event_tooltip": "Перейдіть на сторінку Мережа даного вузла",
"new_version_available": "Доступна нова версія.",
"report_guid": "Довідник сповіщень:",
"report_guid_missing": "Пов’язане сповіщення не знайдено. Існує невелика затримка між нещодавно надісланими сповіщеннями та їх доступністю. Оновіть сторінку та кеш через кілька секунд. Також можливо, вибране сповіщення було видалено під час обслуговування, як зазначено в параметрі <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Натомість відображається останнє сповіщення. Відсутнє сповіщення має такий GUID:",
"report_select_format": "Виберіть формат:",

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "选择语言",
"Maintenance_lang_selector_lable": "选择语言",
"Maintenance_lang_selector_text": "该更改发生在客户端,因此只影响当前浏览器。",
"Maintenance_new_version": "🆕 有新版本可用。查看<a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">发行说明</a>。",
"Maintenance_new_version": "有新版本可用。查看<a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">发行说明</a>。",
"Maintenance_themeselector_apply": "应用",
"Maintenance_themeselector_empty": "选择皮肤",
"Maintenance_themeselector_lable": "选择皮肤",
@@ -716,6 +716,7 @@
"general_event_title": "执行自组织网络事件",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "通知guid",
"report_guid_missing": "未找到链接的通知。最近发送的通知与可用通知之间存在短暂延迟。几秒钟后刷新页面并缓存。所选通知也可能已在维护期间被删除,如 <code>DBCLNP_NOTIFI_HIST</code> 设置中所述。<br/> <br/>系统将改为显示最新通知。缺失的通知具有以下 GUID",
"report_select_format": "选择格式:",
@@ -750,4 +751,4 @@
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
"test_event_icon": "",
"test_event_tooltip": "在测试设置之前,请先保存更改。"
}
}

View File

@@ -7,20 +7,9 @@
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper integrations-plugins">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Plugins');?>
<span class="pageHelp"> <a target="_blank" href="https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins"><i class="fa fa-circle-question"></i></a><span>
</h1>
</section>
<?php
require 'pluginsCore.php';
?>
</div>

View File

@@ -28,8 +28,9 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `APPRISE` | ▶️ | Apprise notification proxy | | | Script | [_publisher_apprise](/front/plugins/_publisher_apprise/) |
| `ARPSCAN` | 🔍 | ARP-scan on current network | | | Script | [arp_scan](/front/plugins/arp_scan/) |
| `AVAHISCAN` | 🆎 | Avahi (mDNS-based) name resolution | | | Script | [avahi_scan](/front/plugins/avahi_scan/) |
| `ASUSWRT` | 🔍 | Import connected devices from AsusWRT | | | Script | [asuswrt_import](/front/plugins/asuswrt_import/) |
| `CSVBCKP` | ⚙ | CSV devices backup | | | Script | [csv_backup](/front/plugins/csv_backup/) |
| `CUSTPROP` | ⚙ | Managing custom device properties values | | Yes | Template | [custom_props](/front/plugins/custom_props/) |
| `CUSTPROP` | ⚙ | Managing custom device properties values | | Yes | Template | [custom_props](/front/plugins/custom_props/) |
| `DBCLNP` | ⚙ | Database cleanup | | Yes* | Script | [db_cleanup](/front/plugins/db_cleanup/) |
| `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](/front/plugins/ddns_update/) |
| `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](/front/plugins/dhcp_leases/) |
@@ -39,8 +40,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](/front/plugins/internet_ip/) |
| `INTRSPD` | ♻ | Internet speed test | | | Script | [internet_speedtest](/front/plugins/internet_speedtest/) |
| `IPNEIGH` | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](/front/plugins/ipneigh/) |
| `LUCIRPC` | 🔍 | Import connected devices from OpenWRT | | | Script | [luci_import](/front/plugins/luci_import/) |
| `ASUSWRT` | 🔍 | Import connected devices from AsusWRT | | | Script | [asuswrt_import](/front/plugins/asuswrt_import/) |
| `LUCIRPC` | 🔍 | Import connected devices from OpenWRT | | | Script | [luci_import](/front/plugins/luci_import/) |
| `MAINT` | ⚙ | Maintenance of logs, etc. | | | Script | [maintenance](/front/plugins/maintenance/) |
| `MQTT` | ▶️ | MQTT for synching to Home Assistant | | | Script | [_publisher_mqtt](/front/plugins/_publisher_mqtt/) |
| `NBTSCAN` | 🆎 | Nbtscan (NetBIOS-based) name resolution | | | Script | [nbtscan_scan](/front/plugins/nbtscan_scan/) |
@@ -57,7 +57,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `SETPWD` | ⚙ | Set password | | Yes | Template | [set_password](/front/plugins/set_password/) |
| `SMTP` | ▶️ | Email notifications | | | Script | [_publisher_email](/front/plugins/_publisher_email/) |
| `SNMPDSC` | 🔍/📥 | SNMP device import & sync | | | Script | [snmp_discovery](/front/plugins/snmp_discovery/) |
| `SYNC` | 🔍/⚙/📥| Sync & import from NetAlertX instances | 🖧 🔄 | Yes | Script | [sync](/front/plugins/sync/) |
| `SYNC` | 🔍/⚙/📥| Sync & import from NetAlertX instances | 🖧 🔄 | Yes | Script | [sync](/front/plugins/sync/) |
| `TELEGRAM` | ▶️ | Telegram notifications | | | Script | [_publisher_telegram](/front/plugins/_publisher_telegram/) |
| `UI` | ♻ | UI specific settings | | Yes | Template | [ui_settings](/front/plugins/ui_settings/) |
| `UNDIS` | 🔍/📥 | Create dummy devices ❌ | | | Script | [undiscoverables](/front/plugins/undiscoverables/) |
@@ -69,7 +69,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
> \* The database cleanup plugin (`DBCLNP`) is not _required_ but the app will become unusable after a while if not executed.
> \** The Undiscoverables plugin (`UNDIS`) inserts only user-specified dummy devices.
> \*\* The Undiscoverables plugin (`UNDIS`) inserts only user-specified dummy devices.
> ❌ marked for removal
> ⌚It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.

View File

@@ -20,4 +20,8 @@ To set up the plugin correctly, make sure...
### Notes
- Additional notes, limitations, Author info.
- Additional notes, limitations, Author info.
- Version: 1.0.0
- Author: `<your github handle>`
- Release Date: `<release date>`

View File

@@ -15,6 +15,7 @@ This Plugin is using awesome [asusrouter](https://github.com/Vaskivskyi/asusrout
## Other info
Version: 1.0.0
Date: 16.1.2025
Author: @labmonkey
- Version: 1.0.0
- Author: [labmonkey](https://github.com/labmonkey)
- Release Date: 16.1.2025

View File

@@ -207,7 +207,7 @@
"description": [
{
"language_code": "en_us",
"string": "Router ip(do not include <code>http://</code> or <code>https://</code>)."
"string": "Router ip(do not include port, <code>http://</code> or <code>https://</code>)."
}
]
},
@@ -242,6 +242,37 @@
}
]
},
{
"function": "port",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [],
"transformers": []
}
]
},
"default_value": "",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Router port"
}
],
"description": [
{
"language_code": "en_us",
"string": "Router port. Leave empty for default."
}
]
},
{
"function": "password",
"type": {

View File

@@ -77,8 +77,13 @@ def get_device_data():
# Create aiohttp session
session = aiohttp.ClientSession(loop=loop)
port = get_setting_value("ASUSWRT_port").strip()
mylog("verbose", [f"[{pluginName}] Connecting to the Router..."])
router = AsusRouter( # required - both IP and URL supported
hostname=get_setting_value("ASUSWRT_host"), # required
port=(None if not port else port), # optional
username=get_setting_value("ASUSWRT_user"), # required
password=get_setting_value("ASUSWRT_password"), # required
use_ssl=get_setting_value("ASUSWRT_ssl"), # optional
@@ -86,14 +91,9 @@ def get_device_data():
)
# Connect to the router
# Throws an error in case of failure
loop.run_until_complete(router.async_connect())
if router.connected:
mylog("verbose", [f"[{pluginName}] logged in successfully."])
else:
mylog("error", [f"[{pluginName}] failed to login."])
return []
# Now you can use the router object to call methods
clients = loop.run_until_complete(router.async_get_data(AsusData.CLIENTS))

View File

@@ -50,7 +50,8 @@ If the Freebox is your gateway you need to find its HTTPS (or HTTP if you prefer
As address, you can either use the public IP of the Freebox, or the unique domain name you found on http://mafreebox.freebox.fr:80/api_version listed as `api_domain`.
### Other info
## Other info
- Author : [KayJay7](https://github.com/KayJay7) & [Lucide](https://github.com/Lucide)
- Date : 2-Dec-2024 - version 1.0
- Version: 1.0
- Author: [KayJay7](https://github.com/KayJay7) & [Lucide](https://github.com/Lucide)
- Release Date: 2-Dec-2024

View File

@@ -5,3 +5,9 @@ Plugin for pinging existing devices via the [ping](https://linux.die.net/man/8/p
### Usage
- Check the Settings page for details.
## Other info
- Version: 1.0.0
- Author: [jokob-sk](https://github.com/jokob-sk)
- Release Date: 19.1.2025

View File

@@ -8,3 +8,10 @@ Plugin to run regular Internet connectivity and IP checks. Change the [dig utili
### Usage
- Check the Settings page for details.
## Other info
- Version: 1.0.0
- Author: [jokob-sk](https://github.com/jokob-sk)
- Release Date: 1.1.2023

View File

@@ -68,8 +68,6 @@ def main():
new_internet_IP, cmd_output = check_internet_IP( PREV_IP, DIG_GET_IP_ARG)
#todo: use `curl ifconfig.me/ip` if above fails
if new_internet_IP == no_internet_ip:
time.sleep(1*i) # Exponential backoff strategy
else:

View File

@@ -21,7 +21,8 @@ To set up the plugin correctly, make sure to add in the plugin settings the name
- `ARPSCAN` does a better job at discovering IPv4 devices because it explicitly sends arp requests
- IPv6 devices will often have multiple addresses, but the ping answer will contain only one. This means that in general this plugin will not discover every address but only those who answer
### Other info
## Other info
- Author : [KayJay7](https://github.com/KayJay7)
- Date : 31-Nov-2024 - version 1.0
- Version: 1.0
- Author: [KayJay7](https://github.com/KayJay7)
- Release Date: 31-Nov-2024

View File

@@ -4,6 +4,7 @@ The plugin is used to import connected devices from OpenWRT
### Other info
- Author : [vaga9938](https://github.com/vaga9938)
- Date : 28-Dec-2024 - version 1.0
- Version: 1.0
- Author: [vaga9938](https://github.com/vaga9938)
- Release Date: 28-Dec-2024

View File

@@ -65,7 +65,8 @@ can not fix some of tplinks OMADA SDN own limitations/bugs:
### Other info
## Other info
- Version: 1.0
- Author : [Flying Toto](https://github.com/FlyingToto)
- Date : 04-Jul-2024 - version 1.0
- Release Date: 04-Jul-2024

View File

@@ -36,8 +36,6 @@ import subprocess
import multiprocessing
# import netifaces
# Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])

View File

@@ -146,7 +146,7 @@
"description": [
{
"language_code": "en_us",
"string": "Encryption key used to encrypt the data before sending and for decryption on the hub. The key needs to be the same on the hub and on the nodes."
"string": "The encryption key is used to secure data by encrypting it before transmission and decrypting it upon arrival at the hub. For the system to function correctly, the encryption key must be identical on both the hub and all the nodes. Similarly, the <code>API_TOKEN</code> must also be set to the same value across the hub and all the nodes to ensure proper authentication and communication."
}
]
},
@@ -222,7 +222,7 @@
"description": [
{
"language_code": "en_us",
"string": "If specified, the hub will pull Devices data from the listed nodes."
"string": "If specified, the hub will pull Devices data from the listed nodes. The <code>API_TOKEN</code> and <code>SYNC_encryption_key</code> must be set to the same value across the hub and all the nodes to ensure proper authentication and communication."
}
]
},

View File

@@ -19,14 +19,6 @@
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-calendar"></i>
<?= lang('Presence_Title');?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">

View File

@@ -1,31 +1,14 @@
<?php
#---------------------------------------------------------------------------------#
# NetAlertX #
# Open Source Network Guard / WIFI & LAN intrusion detector #
# #
# report.php - Front module. Server side. Manage Devices #
#---------------------------------------------------------------------------------# #
# jokob-sk 2022 jokob.sk@gmail.com GNU GPLv3 #
# leiweibau 2022 https://github.com/leiweibau GNU GPLv3 #
# cvc90 2023 https://github.com/cvc90 GNU GPLv3 #
#---------------------------------------------------------------------------------#
require 'php/templates/header.php';
require 'php/templates/notification.php';
?>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-paper-plane"></i>
<?= lang('Navigation_Report') ;?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content tab-content">

View File

@@ -59,35 +59,11 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div id="settingsPage" class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<div class="col-sm-5">
<h1 id="pageTitle col-sm-3">
<i class="fa fa-cog"></i>
<?= lang('Navigation_Settings');?>
<a style="cursor:pointer">
<span>
<i id='toggleSettings' onclick="toggleAllSettings()" class="settings-expand-icon fa fa-angle-double-down"></i>
</span>
</a>
</h1>
</div>
<div class="col-sm-7 settingsImportedTimestamp" title="<?= lang("settings_imported");?> ">
<div class="settingsImported ">
<?= lang("settings_imported_label");?>:
<span id="lastImportedTime"></span>
</div>
</div>
</section>
<section class="content-header">
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" >
<!-- Settings imported time -->
<a data-toggle="collapse" href="#settingsOverview">
<div class ="settings-group col-sm-12 panel-heading panel-title">
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?>
@@ -142,6 +118,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<section class=" padding-bottom col-sm-12">
<!-- needed so the filter & save button don't hide the settings -->
<!-- Settings imported time -->
<div class="col-sm-7 settingsImportedTimestamp" style="display:none" title="<?= lang("settings_imported");?> ">
<div class="settingsImported ">
<?= lang("settings_imported_label");?>:
<span id="lastImportedTime"></span>
</div>
</div>
</section>

View File

@@ -13,21 +13,13 @@
require 'php/templates/header.php';
?>
<?php require 'php/templates/notification.php'; ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<?php require 'php/templates/notification.php'; ?>
<h1 id="pageTitle">
<i class="fa fa-microchip"></i>
<?= lang('SYSTEM_TITLE') ;?>
</h1>
</section>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">

View File

@@ -11,18 +11,6 @@ require 'php/templates/header.php';
<div id="notifications" class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header ">
<h1 id="pageTitle">
<i class="fa fa-bell"></i>
<?= lang('Navigation_Notifications');?>
</h1>
</section>
<section class="content">
<div class="box box-gray col-xs-12" >
<div class="box-header">

View File

@@ -9,16 +9,6 @@
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
<i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Workflows');?>
<span class="pageHelp"> <a target="_blank" href="https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins"><i class="fa fa-circle-question"></i></a><span>
</h1>
</section>
<?php
require 'appEventsCore.php';
?>

View File

@@ -1,5 +1,2 @@
# Schedule cron jobs
* * * * * /app/back/cron_script.sh
#* * * * * echo "$(date +'%Y-%m-%d %H:%M:%S') - Cron job ran" >> /app/log/cron_timestamp.log

15
install/freebox_certificate.pem Executable file
View File

@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICOjCCAcCgAwIBAgIUI0Tu7zsrBJACQIZgLMJobtbdNn4wCgYIKoZIzj0EAwIw
TDELMAkGA1UEBhMCSVQxDjAMBgNVBAgMBUl0YWx5MQ4wDAYDVQQKDAVJbGlhZDEd
MBsGA1UEAwwUSWxpYWRib3ggRUNDIFJvb3QgQ0EwHhcNMjAxMTI3MDkzODEzWhcN
NDAxMTIyMDkzODEzWjBMMQswCQYDVQQGEwJJVDEOMAwGA1UECAwFSXRhbHkxDjAM
BgNVBAoMBUlsaWFkMR0wGwYDVQQDDBRJbGlhZGJveCBFQ0MgUm9vdCBDQTB2MBAG
ByqGSM49AgEGBSuBBAAiA2IABMryJyb2loHNAioY8IztN5MI3UgbVHVP/vZwcnre
ZvJOyDvE4HJgIti5qmfswlnMzpNbwf/MkT+7HAU8jJoTorRm1wtAnQ9cWD3Ebv79
RPwtjjy3Bza3SgdVxmd6fWPUKaNjMGEwHQYDVR0OBBYEFDUij/4lpoJ+kOXRyrcM
jf2RPzOqMB8GA1UdIwQYMBaAFDUij/4lpoJ+kOXRyrcMjf2RPzOqMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQC6eUV1
pFh4UpJOTc1JToztN4ttnQR6rIzxMZ6mNCe+nhjkohWp24pr7BpUYSbEizYCMAQ6
LCiBKV2j7QQGy7N1aBmdur17ZepYzR1YV0eI+Kd978aZggsmhjXENQYVTmm/XA==
-----END CERTIFICATE-----

View File

@@ -30,5 +30,5 @@ source myenv/bin/activate
update-alternatives --install /usr/bin/python python /usr/bin/python3 10
# install packages thru pip3
pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git
pip3 install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git

View File

@@ -17,8 +17,9 @@ For more details, see the [Checkmk Local Checks Documentation](https://docs.chec
### Other info
- Date : 08-Jan-2025 - version 1.0
- Version: 1.0
- Author: N/A
- Release Date: 08-Jan-2025
> [!NOTE]
> This is a community supplied script and not maintained.

View File

@@ -34,7 +34,8 @@ For each MAC or IP address provided, the script:
### Other info
- Date : 23-Dec-2024 - version 1.0
- Version: 1.0
- Release Date: 23-Dec-2024
- Author: [laxduke](https://github.com/laxduke)

View File

@@ -25,7 +25,8 @@ import subprocess
import conf
from const import *
from logger import mylog
from helper import filePermissions, timeNowTZ, updateState, get_setting_value
from helper import filePermissions, timeNowTZ, get_setting_value
from app_state import updateState
from api import update_api
from networkscan import process_scan
from initialise import importConfigs
@@ -89,7 +90,7 @@ def main ():
while True:
# re-load user configuration and plugins
all_plugins = importConfigs(db, all_plugins)
all_plugins, imported = importConfigs(db, all_plugins)
# update time started
conf.loop_start_time = timeNowTZ()
@@ -98,11 +99,11 @@ def main ():
# Handle plugins executed ONCE
if conf.plugins_once_run == False:
pluginsState = run_plugin_scripts(db, all_plugins, 'once')
run_plugin_scripts(db, all_plugins, 'once')
conf.plugins_once_run = True
# check if there is a front end initiated event which needs to be executed
pluginsState = check_and_run_user_event(db, all_plugins, pluginsState)
# check if user is waiting for api_update
check_and_run_user_event(db, all_plugins)
# Update API endpoints
update_api(db, all_plugins, False)
@@ -121,27 +122,27 @@ def main ():
startTime = startTime.replace (microsecond=0)
# Check if any plugins need to run on schedule
pluginsState = run_plugin_scripts(db, all_plugins, 'schedule', pluginsState)
run_plugin_scripts(db, all_plugins, 'schedule')
# determine run/scan type based on passed time
# --------------------------------------------
# Runs plugin scripts which are set to run every timne after a scans finished
pluginsState = run_plugin_scripts(db, all_plugins, 'always_after_scan', pluginsState)
# Runs plugin scripts which are set to run every time after a scans finished
run_plugin_scripts(db, all_plugins, 'always_after_scan')
# process all the scanned data into new devices
mylog('debug', [f'[MAIN] processScan: {pluginsState.processScan}'])
processScan = updateState("Check scan").processScan
mylog('debug', [f'[MAIN] processScan: {processScan}'])
if pluginsState.processScan == True:
mylog('debug', "[MAIN] start processig scan results")
pluginsState.processScan = False
if processScan == True:
mylog('debug', "[MAIN] start processig scan results")
process_scan(db)
updateState("Scan processed", None, None, None, None, False)
# --------
# Reporting
# run plugins before notification processing (e.g. Plugins to discover device names)
pluginsState = run_plugin_scripts(db, all_plugins, 'before_name_updates', pluginsState)
run_plugin_scripts(db, all_plugins, 'before_name_updates')
# Resolve devices names
mylog('debug','[Main] Resolve devices names')
@@ -155,7 +156,7 @@ def main ():
# new devices were found
if len(newDevices) > 0:
# run all plugins registered to be run when new devices are found
pluginsState = run_plugin_scripts(db, all_plugins, 'on_new_device', pluginsState)
run_plugin_scripts(db, all_plugins, 'on_new_device')
# Notification handling
# ----------------------------------------
@@ -170,12 +171,10 @@ def main ():
# run all enabled publisher gateways
if notificationObj.HasNotifications:
pluginsState = run_plugin_scripts(db, all_plugins, 'on_notification', pluginsState)
run_plugin_scripts(db, all_plugins, 'on_notification')
notification.setAllProcessed()
notification.clearPendingEmailFlag()
else:
mylog('verbose', ['[Notification] No changes to report'])

View File

@@ -7,8 +7,9 @@ import datetime
import conf
from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all, sql_online_history, sql_devices_tiles)
from logger import mylog
from helper import write_file, get_setting_value, updateState, timeNowTZ
from execution_log import ExecutionLog
from helper import write_file, get_setting_value, timeNowTZ
from app_state import updateState
from user_events_queue import UserEventsQueue
from notification import write_notification
# Import the start_server function
@@ -31,12 +32,10 @@ def update_api(db, all_plugins, forceUpdate, updateOnlyDataSources=[], is_ad_hoc
start_periodic_write(interval=1)
# Update app_state.json and retrieve app_state to check if GraphQL server is running
app_state = updateState("Update: API", None, None, None, None)
app_state = updateState()
folder = apiPath
# Save plugins
write_file(folder + 'plugins.json', json.dumps({"data": all_plugins}))
write_file(apiPath + 'plugins.json', json.dumps({"data": all_plugins}))
# Prepare database tables we want to expose
dataSourcesSQLs = [
@@ -57,7 +56,7 @@ def update_api(db, all_plugins, forceUpdate, updateOnlyDataSources=[], is_ad_hoc
# Save selected database tables
for dsSQL in dataSourcesSQLs:
if not updateOnlyDataSources or dsSQL[0] in updateOnlyDataSources:
api_endpoint_class(db, forceUpdate, dsSQL[1], folder + 'table_' + dsSQL[0] + '.json', is_ad_hoc_user_event)
api_endpoint_class(db, forceUpdate, dsSQL[1], apiPath + 'table_' + dsSQL[0] + '.json', is_ad_hoc_user_event)
# Start the GraphQL server
graphql_port_value = get_setting_value("GRAPHQL_PORT")
@@ -87,7 +86,7 @@ class api_endpoint_class:
self.path = path
self.fileName = path.split('/')[-1]
self.hash = hash(json.dumps(self.jsonData))
self.debounce_interval = 5 # Time to wait before writing
self.debounce_interval = 3 # Time in seconds to wait before writing
self.changeDetectedWhen = None
# self.last_update_time = current_time - datetime.timedelta(minutes=1) # Last time data was updated
self.is_ad_hoc_user_event = is_ad_hoc_user_event
@@ -147,7 +146,7 @@ class api_endpoint_class:
# Update user event execution log
# mylog('verbose', [f'[API] api_endpoint_class: is_ad_hoc_user_event {self.is_ad_hoc_user_event}'])
if self.is_ad_hoc_user_event:
execution_log = ExecutionLog()
execution_log = UserEventsQueue()
execution_log.finalize_event("update_api")
self.is_ad_hoc_user_event = False

109
server/app_state.py Executable file
View File

@@ -0,0 +1,109 @@
import os
import json
import conf
from const import *
from logger import mylog, logResult
from helper import timeNowTZ, timeNow, checkNewVersion
# Register NetAlertX directories
INSTALL_PATH="/app"
#-------------------------------------------------------------------------------
# App state
#-------------------------------------------------------------------------------
# A class to manage the application state and to provide a frontend accessible API point
# To keep an existing value pass None
class app_state_class:
def __init__(self, currentState = None, settingsSaved=None, settingsImported=None, showSpinner=False, graphQLServerStarted=0, processScan=False):
# json file containing the state to communicate with the frontend
stateFile = apiPath + 'app_state.json'
previousState = ""
# Update self
self.lastUpdated = str(timeNowTZ())
if os.path.exists(stateFile):
try:
with open(stateFile, 'r') as json_file:
previousState = json.load(json_file)
except json.decoder.JSONDecodeError as e:
mylog('none', [f'[app_state_class] Failed to handle app_state.json: {e}'])
# Check if the file exists and recover previous values
if previousState != "":
self.settingsSaved = previousState.get("settingsSaved", 0)
self.settingsImported = previousState.get("settingsImported", 0)
self.processScan = previousState.get("processScan", False)
self.showSpinner = previousState.get("showSpinner", False)
self.isNewVersion = previousState.get("isNewVersion", False)
self.isNewVersionChecked = previousState.get("isNewVersionChecked", 0)
self.graphQLServerStarted = previousState.get("graphQLServerStarted", 0)
self.currentState = previousState.get("currentState", "Init")
else: # init first time values
self.settingsSaved = 0
self.settingsImported = 0
self.showSpinner = False
self.processScan = False
self.isNewVersion = checkNewVersion()
self.isNewVersionChecked = int(timeNow().timestamp())
self.graphQLServerStarted = 0
self.currentState = "Init"
# Overwrite with provided parameters if supplied
if settingsSaved is not None:
self.settingsSaved = settingsSaved
if settingsImported is not None:
self.settingsImported = settingsImported
if showSpinner is not None:
self.showSpinner = showSpinner
if graphQLServerStarted is not None:
self.graphQLServerStarted = graphQLServerStarted
if processScan is not None:
self.processScan = processScan
if currentState is not None:
self.currentState = currentState
# check for new version every hour and if currently not running new version
if self.isNewVersion is False and self.isNewVersionChecked + 3600 < int(timeNow().timestamp()):
self.isNewVersion = checkNewVersion()
self.isNewVersionChecked = int(timeNow().timestamp())
# Update .json file
# with open(stateFile, 'w') as json_file:
# json.dump(self, json_file, cls=AppStateEncoder, indent=4)
# Remove lastUpdated from the dictionary for comparison
currentStateDict = self.__dict__.copy()
currentStateDict.pop('lastUpdated', None)
# Compare current state with previous state before updating
if previousState != currentStateDict:
# Sanity check before saving the .json file
try:
json_data = json.dumps(self, cls=AppStateEncoder, indent=4)
with open(stateFile, 'w') as json_file:
json_file.write(json_data)
except (TypeError, ValueError) as e:
mylog('none', [f'[app_state_class] Failed to serialize object to JSON: {e}'])
return # Allows chaining by returning self
#-------------------------------------------------------------------------------
# method to update the state
def updateState(newState = None, settingsSaved = None, settingsImported = None, showSpinner = False, graphQLServerStarted = None, processScan = None):
return app_state_class(newState, settingsSaved, settingsImported, showSpinner, graphQLServerStarted, processScan)
#-------------------------------------------------------------------------------
# Checks if the object has a __dict__ attribute. If it does, it assumes that it's an instance of a class and serializes its attributes dynamically.
class AppStateEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, '__dict__'):
# If the object has a '__dict__', assume it's an instance of a class
return obj.__dict__
return super().default(obj)

View File

@@ -8,7 +8,7 @@ import json
from const import fullDbPath, sql_devices_stats, sql_devices_all, sql_generateGuid
from logger import mylog
from helper import json_obj, initOrSetParam, row_to_json, timeNowTZ#, split_string #, updateState
from helper import json_obj, initOrSetParam, row_to_json, timeNowTZ
from appevent import AppEvent_obj
class DB():

View File

@@ -9,7 +9,8 @@ INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
from logger import mylog
from helper import get_setting_value, timeNowTZ, updateState
from helper import get_setting_value, timeNowTZ
from app_state import updateState
from notification import write_notification
# Flask application

View File

@@ -52,94 +52,6 @@ def get_timezone_offset():
return offset_formatted
#-------------------------------------------------------------------------------
# App state
#-------------------------------------------------------------------------------
# A class to manage the application state and to provide a frontend accessible API point
# To keep an existing value pass None
class app_state_class:
def __init__(self, currentState, settingsSaved=None, settingsImported=None, showSpinner=False, graphQLServerStarted=0):
# json file containing the state to communicate with the frontend
stateFile = apiPath + '/app_state.json'
previousState = ""
# if currentState == 'Initializing':
# checkNewVersion(False)
# Update self
self.currentState = currentState
self.lastUpdated = str(timeNowTZ())
if os.path.exists(stateFile):
try:
with open(stateFile, 'r') as json_file:
previousState = json.load(json_file)
except json.decoder.JSONDecodeError as e:
mylog('none', [f'[app_state_class] Failed to handle app_state.json: {e}'])
# Check if the file exists and recover previous values
if previousState != "":
self.settingsSaved = previousState.get("settingsSaved", 0)
self.settingsImported = previousState.get("settingsImported", 0)
self.showSpinner = previousState.get("showSpinner", False)
self.isNewVersion = previousState.get("isNewVersion", False)
self.isNewVersionChecked = previousState.get("isNewVersionChecked", 0)
self.graphQLServerStarted = previousState.get("graphQLServerStarted", 0)
else: # init first time values
self.settingsSaved = 0
self.settingsImported = 0
self.showSpinner = False
self.isNewVersion = checkNewVersion()
self.isNewVersionChecked = int(timeNow().timestamp())
self.graphQLServerStarted = 0
# Overwrite with provided parameters if supplied
if settingsSaved is not None:
self.settingsSaved = settingsSaved
if settingsImported is not None:
self.settingsImported = settingsImported
if showSpinner is not None:
self.showSpinner = showSpinner
if graphQLServerStarted is not None:
self.graphQLServerStarted = graphQLServerStarted
# check for new version every hour and if currently not running new version
if self.isNewVersion is False and self.isNewVersionChecked + 3600 < int(timeNow().timestamp()):
self.isNewVersion = checkNewVersion()
self.isNewVersionChecked = int(timeNow().timestamp())
# Update .json file
# with open(stateFile, 'w') as json_file:
# json.dump(self, json_file, cls=AppStateEncoder, indent=4)
# Sanity check before saving the .json file
try:
json_data = json.dumps(self, cls=AppStateEncoder, indent=4)
with open(stateFile, 'w') as json_file:
json_file.write(json_data)
except (TypeError, ValueError) as e:
mylog('none', [f'[app_state_class] Failed to serialize object to JSON: {e}'])
def isSet(self):
result = False
if self.currentState != "":
result = True
return result
#-------------------------------------------------------------------------------
# method to update the state
def updateState(newState, settingsSaved = None, settingsImported = None, showSpinner = False, graphQLServerStarted = None):
return app_state_class(newState, settingsSaved, settingsImported, showSpinner, graphQLServerStarted)
#-------------------------------------------------------------------------------
def updateSubnets(scan_subnets):
subnets = []
@@ -887,14 +799,6 @@ def add_json_list (row, list):
return list
#-------------------------------------------------------------------------------
# Checks if the object has a __dict__ attribute. If it does, it assumes that it's an instance of a class and serializes its attributes dynamically.
class AppStateEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, '__dict__'):
# If the object has a '__dict__', assume it's an instance of a class
return obj.__dict__
return super().default(obj)
#-------------------------------------------------------------------------------
# Checks if the object has a __dict__ attribute. If it does, it assumes that it's an instance of a class and serializes its attributes dynamically.

View File

@@ -12,7 +12,8 @@ import re
# Register NetAlertX libraries
import conf
from const import fullConfPath, applicationPath, fullConfFolder
from helper import fixPermissions, collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, updateState, setting_value_to_python_type, timeNowTZ, get_setting_value, generate_random_string
from helper import fixPermissions, collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, setting_value_to_python_type, timeNowTZ, get_setting_value, generate_random_string
from app_state import updateState
from logger import mylog
from api import update_api
from scheduler import schedule_class
@@ -133,7 +134,7 @@ def importConfigs (db, all_plugins):
if (fileModifiedTime == conf.lastImportedConfFile) and all_plugins is not None:
mylog('debug', ['[Import Config] skipping config file import'])
return all_plugins
return all_plugins, False
# Header
updateState("Import config", showSpinner = True)
@@ -413,7 +414,7 @@ def importConfigs (db, all_plugins):
# front end app log loggging
write_notification(msg, 'info', timeNowTZ())
return all_plugins
return all_plugins, True

View File

@@ -12,11 +12,12 @@ from collections import namedtuple
import conf
from const import pluginsPath, logPath, applicationPath, reportTemplatesPath
from logger import mylog, Logger
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value
from helper import timeNowTZ, get_file_content, write_file, get_setting, get_setting_value
from app_state import updateState
from api import update_api
from plugin_utils import logEventStatusCounts, get_plugin_string, get_plugin_setting_obj, print_plugin_info, list_to_csv, combine_plugin_objects, resolve_wildcards_arr, handle_empty, custom_plugin_decoder, decode_and_rename_files
from notification import Notification_obj, write_notification
from execution_log import ExecutionLog
from user_events_queue import UserEventsQueue
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
@@ -102,12 +103,7 @@ class plugin_param:
self.multiplyTimeout = multiplyTimeout
#-------------------------------------------------------------------------------
class plugins_state:
def __init__(self, processScan = False):
self.processScan = processScan
#-------------------------------------------------------------------------------
def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state()):
def run_plugin_scripts(db, all_plugins, runType):
# Header
updateState("Run: Plugins")
@@ -140,7 +136,7 @@ def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state())
print_plugin_info(plugin, ['display_name'])
mylog('debug', ['[Plugins] CMD: ', get_plugin_setting_obj(plugin, "CMD")["value"]])
pluginsState = execute_plugin(db, all_plugins, plugin, pluginsState)
execute_plugin(db, all_plugins, plugin)
# update last run time
if runType == "schedule":
for schd in conf.mySchedules:
@@ -148,9 +144,6 @@ def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state())
# note the last time the scheduled plugin run was executed
schd.last_run = timeNowTZ()
return pluginsState
# Function to run a plugin command
def run_plugin(command, set_RUN_TIMEOUT, plugin):
@@ -167,20 +160,15 @@ def run_plugin(command, set_RUN_TIMEOUT, plugin):
#-------------------------------------------------------------------------------
# Executes the plugin command specified in the setting with the function specified as CMD
def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
def execute_plugin(db, all_plugins, plugin ):
sql = db.sql
if pluginsState is None:
mylog('debug', ['[Plugins] pluginsState is None'])
pluginsState = plugins_state()
# ------- necessary settings check --------
set = get_plugin_setting_obj(plugin, "CMD")
# handle missing "function":"CMD" setting
if set == None:
return pluginsState
return
set_CMD = set["value"]
@@ -394,7 +382,7 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
# handle missing "function":"DB_PATH" setting
if set == None:
mylog('none', ['[Plugins] ⚠ ERROR: DB_PATH setting for plugin type sqlite-db-query missing.'])
return pluginsState
return
fullSqlitePath = set["value"]
@@ -408,7 +396,7 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
except sqlite3.Error as e:
mylog('none',[f'[Plugins] ⚠ ERROR: DB_PATH setting ({fullSqlitePath}) for plugin {plugin["unique_prefix"]}. Did you mount it correctly?'])
mylog('none',[f'[Plugins] ⚠ ERROR: ATTACH DATABASE failed with SQL ERROR: ', e])
return pluginsState
return
for row in arr:
# There has to be always 9 or 13 columns
@@ -459,7 +447,7 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
# check if the subprocess / SQL query failed / there was no valid output
if len(sqlParams) == 0:
mylog('none', [f'[Plugins] No output received from the plugin "{plugin["unique_prefix"]}"'])
return pluginsState
return
else:
mylog('verbose', ['[Plugins] SUCCESS, received ', len(sqlParams), ' entries'])
mylog('debug', ['[Plugins] sqlParam entries: ', sqlParams])
@@ -468,17 +456,24 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
if len(sqlParams) > 0:
# create objects
pluginsState = process_plugin_events(db, plugin, pluginsState, sqlParams)
process_plugin_events(db, plugin, sqlParams)
# update API endpoints
update_api(db, all_plugins, False, ["plugins_events","plugins_objects", "plugins_history", "appevents"])
endpoints = ["plugins_events","plugins_objects", "plugins_history", "appevents"]
# check if we need to update devices api endpoint as well to prevent long user waits on Loading...
userUpdatedDevices = UserEventsQueue().has_update_devices
if userUpdatedDevices:
endpoints += ["devices"]
update_api(db, all_plugins, True, endpoints, userUpdatedDevices)
return pluginsState
return
#-------------------------------------------------------------------------------
# Check if watched values changed for the given plugin
def process_plugin_events(db, plugin, pluginsState, plugEventsArr):
def process_plugin_events(db, plugin, plugEventsArr):
sql = db.sql
@@ -780,13 +775,14 @@ def process_plugin_events(db, plugin, pluginsState, plugEventsArr):
db.commitDB()
# perform scan if mapped to CurrentScan table
if dbTable == 'CurrentScan':
pluginsState.processScan = True
if dbTable == 'CurrentScan':
updateState("Process scan: True", None, None, None, None, True) # set processScan = True in the appState
db.commitDB()
return pluginsState
return
#-------------------------------------------------------------------------------
@@ -844,14 +840,15 @@ class plugin_object_class:
self.watchedHash = str(hash(tmp))
#===============================================================================
# Handling of user initialized front-end events
#===============================================================================
def check_and_run_user_event(db, all_plugins, pluginsState):
def check_and_run_user_event(db, all_plugins):
"""
Process user events from the execution queue log file and notify the user about executed events.
"""
execution_log = ExecutionLog()
execution_log = UserEventsQueue()
# Track whether to show notification for executed events
executed_events = []
@@ -859,7 +856,10 @@ def check_and_run_user_event(db, all_plugins, pluginsState):
# Read the log file to get the lines
lines = execution_log.read_log()
if not lines:
return pluginsState # Exit early if the log file is empty
mylog('debug', ['[check_and_run_user_event] User Execution Queue is empty'])
return # Exit early if the log file is empty
else:
mylog('debug', ['[check_and_run_user_event] Process User Execution Queue:' + ', '.join(map(str, lines))])
for line in lines:
# Extract event name and parameters from the log line
@@ -871,11 +871,11 @@ def check_and_run_user_event(db, all_plugins, pluginsState):
# Process each event type
if event == 'test':
pluginsState = handle_test(param, db, all_plugins, pluginsState)
handle_test(param, db, all_plugins)
executed_events.append(f"test with param {param}")
execution_log.finalize_event("test")
elif event == 'run':
pluginsState = handle_run(param, db, all_plugins, pluginsState)
handle_run(param, db, all_plugins)
executed_events.append(f"run with param {param}")
execution_log.finalize_event("run")
elif event == 'update_api':
@@ -892,27 +892,27 @@ def check_and_run_user_event(db, all_plugins, pluginsState):
mylog('minimal', ['[check_and_run_user_event] INFO: Executed events: ', executed_events_message])
write_notification(f"[Ad-hoc events] Events executed: {executed_events_message}", "interrupt", timeNowTZ())
return pluginsState
return
#-------------------------------------------------------------------------------
def handle_run(runType, db, all_plugins, pluginsState):
def handle_run(runType, db, all_plugins):
mylog('minimal', ['[', timeNowTZ(), '] START Run: ', runType])
# run the plugin to run
for plugin in all_plugins:
if plugin["unique_prefix"] == runType:
pluginsState = execute_plugin(db, all_plugins, plugin, pluginsState)
execute_plugin(db, all_plugins, plugin)
mylog('minimal', ['[', timeNowTZ(), '] END Run: ', runType])
return pluginsState
return
#-------------------------------------------------------------------------------
def handle_test(runType, db, all_plugins, pluginsState):
def handle_test(runType, db, all_plugins):
mylog('minimal', ['[', timeNowTZ(), '] [Test] START Test: ', runType])
@@ -924,12 +924,12 @@ def handle_test(runType, db, all_plugins, pluginsState):
notificationObj = notification.create(sample_json, "")
# Run test
pluginsState = handle_run(runType, db, all_plugins, pluginsState)
handle_run(runType, db, all_plugins)
# Remove sample notification
notificationObj.remove(notificationObj.GUID)
mylog('minimal', ['[Test] END Test: ', runType])
return pluginsState
return

View File

@@ -4,7 +4,8 @@ import json
import conf
from logger import mylog
from const import pluginsPath, logPath, apiPath
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value, setting_value_to_python_type
from helper import timeNowTZ, get_file_content, write_file, get_setting, get_setting_value, setting_value_to_python_type
from app_state import updateState
from crypto_utils import decrypt_data
module_name = 'Plugin utils'

View File

@@ -4,7 +4,7 @@ import os
from const import pluginsPath, logPath, applicationPath, reportTemplatesPath
from logger import mylog
class ExecutionLog:
class UserEventsQueue:
"""
Handles the execution queue log file, allowing reading, writing,
and removing processed events.
@@ -14,12 +14,23 @@ class ExecutionLog:
self.log_path = logPath
self.log_file = os.path.join(self.log_path, "execution_queue.log")
def has_update_devices(self):
lines = self.read_log()
for line in lines:
if 'update_api|devices' in line:
return True
return False
def read_log(self):
"""
Reads the log file and returns all lines.
Returns an empty list if the file doesn't exist.
"""
if not os.path.exists(self.log_file):
mylog('none', ['[UserEventsQueue] Log file not found: ', self.log_file])
return [] # No log file, return empty list
with open(self.log_file, "r") as file:
return file.readlines()
@@ -61,7 +72,7 @@ class ExecutionLog:
self.write_log(updated_lines)
mylog('minimal', ['[ExecutionLog] Processed event: ', event])
mylog('minimal', ['[UserEventsQueue] Processed event: ', event])
return removed