Compare commits

...

16 Commits

Author SHA1 Message Date
jokob-sk
c63f424c7d LOADED_PLUGINS docker variable #975
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-24 20:05:42 +11:00
jokob-sk
dd1580e536 asus and logging #972 2025-01-24 18:58:10 +11:00
jokob-sk
630e4f6327 asus DHCPLSS guide #963
Some checks failed
docker / docker_dev (push) Has been cancelled
2025-01-22 08:37:55 +11:00
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
68 changed files with 586 additions and 562 deletions

View File

@@ -12,13 +12,13 @@ jobs:
# Post to Twitter # Post to Twitter
- name: Post to Twitter - name: Post to Twitter
uses: gr2m/twitter-together@v2 uses: twitter-together/action@v3
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} 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: with:
tweet: | tweet: |
🎉 New release: **${{ github.event.release.name }}** is live! 🚀 🎉 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 ARG INSTALL_DIR=/app
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
# Install build dependencies # 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 && python -m venv /opt/venv
# Enable venv # Enable venv
@@ -14,29 +14,16 @@ ENV PATH="/opt/venv/bin:$PATH"
COPY . ${INSTALL_DIR}/ 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 d -exec chmod 750 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \ && 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 {} \;" && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
# Append Iliadbox certificate to aiofreepybox # Append Iliadbox certificate to aiofreepybox
RUN printf "\n-----BEGIN CERTIFICATE-----\n\ RUN cat ${INSTALL_DIR}/install/freebox_certificate.pem >> /opt/venv/lib/python3.12/site-packages/aiofreepybox/freebox_certificates.pem
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
# second stage # second stage
FROM alpine:3.20 AS runner FROM alpine:3.21 AS runner
ARG INSTALL_DIR=/app ARG INSTALL_DIR=/app
@@ -55,10 +42,9 @@ ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
RUN apk update --no-cache \ RUN apk update --no-cache \
&& apk add --no-cache bash zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \ && 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 sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
&& apk add --no-cache python3 nginx \ && apk add --no-cache python3 nginx \
&& apk add --no-cache dcron \
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \ && ln -s /usr/bin/awake /usr/bin/wakeonlan \
&& bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}" \ && bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}" \
&& rm -f /etc/nginx/http.d/default.conf && 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}/ COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/ ${INSTALL_DIR}/
# Add crontab file # Add crontab file
COPY install/crontab /etc/crontabs/root COPY --chmod=600 --chown=root:root install/crontab /etc/crontabs/root
# Start all required services # Start all required services
RUN ${INSTALL_DIR}/dockerfiles/start.sh 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 apt-get install -y python3-venv
RUN python3 -m venv myenv 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 # Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt 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 # Check if there are any entries with cron_restart_backend
if grep -q "cron_restart_backend" "$LOG_FILE"; then if grep -q "cron_restart_backend" "$LOG_FILE"; then
# Kill all python processes (restart handled by s6 overlay) # Restart python application using s6
pkill -f "python " && echo 'done' s6-svc -r /var/run/s6-rc/servicedirs/netalertx
echo 'done'
# Remove all lines containing cron_restart_backend from the log file # Remove all lines containing cron_restart_backend from the log file
sed -i '/cron_restart_backend/d' "$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/workflows.php:/app/front/workflows.php
- ${DEV_LOCATION}/front/appEventsCore.php:/app/front/appEventsCore.php - ${DEV_LOCATION}/front/appEventsCore.php:/app/front/appEventsCore.php
- ${DEV_LOCATION}/front/multiEditCore.php:/app/front/multiEditCore.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 - ${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 # DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -72,4 +71,5 @@ services:
- PORT=${PORT} - PORT=${PORT}
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders # ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL} - ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}
# - LOADED_PLUGINS=["DHCPLSS","PIHOLE","ASUSWRT","FREEBOX"]

View File

@@ -55,7 +55,7 @@ else
echo "Config file saved to ${INSTALL_DIR}/config/app_conf_override.json" echo "Config file saved to ${INSTALL_DIR}/config/app_conf_override.json"
fi fi
# 🔻 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2024 # 🔻 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2025
# Check if pialert.db exists, then create a symbolic link to app.db # Check if pialert.db exists, then create a symbolic link to app.db
if [ -f "${INSTALL_DIR_OLD}/db/${OLD_APP_NAME}.db" ]; then if [ -f "${INSTALL_DIR_OLD}/db/${OLD_APP_NAME}.db" ]; then
@@ -66,7 +66,7 @@ fi
if [ -f "${INSTALL_DIR_OLD}/config/${OLD_APP_NAME}.conf" ]; then if [ -f "${INSTALL_DIR_OLD}/config/${OLD_APP_NAME}.conf" ]; then
ln -s "${INSTALL_DIR_OLD}/config/${OLD_APP_NAME}.conf" "${INSTALL_DIR}/config/${CONF_FILE}" ln -s "${INSTALL_DIR_OLD}/config/${OLD_APP_NAME}.conf" "${INSTALL_DIR}/config/${CONF_FILE}"
fi fi
# 🔺 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2024 # 🔺 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2025
# Copy starter .db and .conf if they don't exist # Copy starter .db and .conf if they don't exist
cp -na "${INSTALL_DIR}/back/${CONF_FILE}" "${INSTALL_DIR}/config/${CONF_FILE}" cp -na "${INSTALL_DIR}/back/${CONF_FILE}" "${INSTALL_DIR}/config/${CONF_FILE}"
@@ -83,6 +83,13 @@ if [ -n "${TZ}" ]; then
echo $TZ > /etc/timezone echo $TZ > /etc/timezone
fi fi
# if custom variables not set we do not need to do anything
if [ -n "${LOADED_PLUGINS}" ]; then
FILECONF="${INSTALL_DIR}/config/${CONF_FILE}"
echo "[INSTALL] Setup custom LOADED_PLUGINS variable"
sed -i "\#^LOADED_PLUGINS=#c\LOADED_PLUGINS=${LOADED_PLUGINS}" "${FILECONF}"
fi
echo "[INSTALL] Setup NGINX" echo "[INSTALL] Setup NGINX"
echo "Setting webserver to address ($LISTEN_ADDR) and port ($PORT)" echo "Setting webserver to address ($LISTEN_ADDR) and port ($PORT)"
envsubst '$INSTALL_DIR $LISTEN_ADDR $PORT' < "${INSTALL_DIR}/install/netalertx.template.conf" > "${NGINX_CONFIG_FILE}" envsubst '$INSTALL_DIR $LISTEN_ADDR $PORT' < "${INSTALL_DIR}/install/netalertx.template.conf" > "${NGINX_CONFIG_FILE}"
@@ -125,10 +132,6 @@ if [ ! -f "${INSTALL_DIR}/front/buildtimestamp.txt" ]; then
chown nginx:www-data "${INSTALL_DIR}/front/buildtimestamp.txt" chown nginx:www-data "${INSTALL_DIR}/front/buildtimestamp.txt"
fi fi
# Start crond service in the background
echo "[INSTALL] Starting crond service..."
crond -f -d 8 > /dev/null 2>&1 &
echo -e " echo -e "
[ENV] PATH is ${PATH} [ENV] PATH is ${PATH}
[ENV] PORT is ${PORT} [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/nginx/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/$APP_NAME/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 "${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\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\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
echo -e '#!/bin/execlineb -P 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 touch /etc/s6-overlay/s6-rc.d/$APP_NAME/dependencies.d/nginx
# this removes the current file # this removes the current file
# rm -f $0 rm -f $0

View File

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

View File

@@ -28,14 +28,6 @@
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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 ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">
@@ -78,6 +70,9 @@
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3> <h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
</div> </div>
<div class="dummyDevice col-md-3 "> <div class="dummyDevice col-md-3 ">
<span id="multiEditPlc">
<!-- multi edit button placeholder -->
</span>
<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> <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> </span>
@@ -106,7 +101,7 @@
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
</section> </section>
<!-- /.content --> <!-- /.content -->
<div id="multiEditPlc" class="col-md-2"></div>
</div> </div>
<!-- /.content-wrapper --> <!-- /.content-wrapper -->
@@ -749,9 +744,9 @@ function initializeDatatable (status) {
// add multi-edit button // add multi-edit button
$('#multiEditPlc').append( $('#multiEditPlc').append(
`<button type="submit" id="multiEdit" class="btn btn-primary" style="display:none" onclick="multiEditDevices();"> `<span type="submit" id="multiEdit" class="pointer " style="display:none" onclick="multiEditDevices();">
<i class="fa fa-pencil pointer" ></i> ${getString("Device_MultiEdit")} <a href="#"><i class="fa fa-pencil " ></i> ${getString("Device_MultiEdit")} </a>
</button>`) </span>`)
// Event listener for row selection in DataTable // Event listener for row selection in DataTable
$('#tableDevices').on('click', 'tr', function (e) { $('#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 <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
?> ?>
@@ -19,26 +5,7 @@
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper eventsPage">
<!-- 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>
<!-- Main content ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">
@@ -123,15 +90,31 @@
<!-- datatable ------------------------------------------------------------- --> <!-- datatable ------------------------------------------------------------- -->
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div id="tableEventsBox" class="box"> <div id="tableEventsBox" class="box">
<!-- box-header --> <!-- box-header -->
<div class="box-header"> <div class="box-header col-xs-12">
<h3 id="tableEventsTitle" class="box-title text-gray">Events</h3> <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> </div>
<!-- table --> <!-- table -->
<div class="box-body table-responsive"> <div class="box-body table-responsive">
<table id="tableEvents" class="table table-bordered table-hover table-striped "> <table id="tableEvents" class="table table-bordered table-hover table-striped ">
<thead> <thead>
<tr> <tr>

View File

@@ -1,18 +1,9 @@
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
require 'php/templates/notification.php';
?> ?>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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 ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">
<h4> <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 <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
require 'php/templates/notification.php';
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper" id="maintenancePage"> <div class="content-wrapper" id="maintenancePage">
<!-- Content header--------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content-header"> <section class="content">
<?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">
<?php <?php
// Size and last mod of DB ------------------------------------------------------ // Size and last mod of DB ------------------------------------------------------
@@ -91,10 +60,11 @@ $db->close();
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_version');?> <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> <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>
<div class="db_info_table_cell"> <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> </div>
<div class="db_info_table_row"> <div class="db_info_table_row">

View File

@@ -1,6 +1,7 @@
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
require 'php/templates/notification.php';
// online / offline badges HTML snippets // online / offline badges HTML snippets
define('badge_online', '<div class="badge bg-green text-white" style="width: 60px;">Online</div>'); define('badge_online', '<div class="badge bg-green text-white" style="width: 60px;">Online</div>');
@@ -15,21 +16,13 @@
// show spinning icon // show spinning icon
showSpinner() showSpinner()
</script> </script>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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> <div id="networkTree" class="drag"></div>

View File

@@ -356,7 +356,10 @@
<!-- Maintenance menu item --> <!-- Maintenance menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active menu-open'; } ?>"> <li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active menu-open'; } ?>">
<a href="#" onclick="openUrl(['./maintenance.php'])"> <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> <i class="fa fa-fw fa-wrench"></i> <span><?= lang('Navigation_Maintenance');?></span>
<span class="pull-right-container"> <span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i> <i class="fa fa-angle-left pull-right"></i>
@@ -430,17 +433,14 @@
</li> </li>
<!-- About menu item --> <!-- 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="#"> <a href="#">
<i class="fa fa-fw fa-info"></i> <span><?= lang('Navigation_About');?></span> <i class="fa fa-fw fa-info"></i> <span><?= lang('Navigation_About');?></span>
<span class="pull-right-container"> <span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i> <i class="fa fa-angle-left pull-right"></i>
</span> </span>
</a> </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';} ?>;"> <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="donations.php"> <?= lang("Navigation_Donations");?> </a>
</li>
<li> <li>
<a href="help_faq.php"> <?= lang("Navigation_HelpFAQ");?> </a> <a href="help_faq.php"> <?= lang("Navigation_HelpFAQ");?> </a>
</li> </li>

View File

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

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Tria idioma", "Maintenance_lang_selector_empty": "Tria idioma",
"Maintenance_lang_selector_lable": "Selecció d'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_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_apply": "Aplica",
"Maintenance_themeselector_empty": "Tria una Skin", "Maintenance_themeselector_empty": "Tria una Skin",
"Maintenance_themeselector_lable": "Selecciona una Skin", "Maintenance_themeselector_lable": "Selecciona una Skin",
@@ -716,6 +716,7 @@
"general_event_title": "Execució d'un esdeveniment ad-hoc", "general_event_title": "Execució d'un esdeveniment ad-hoc",
"go_to_node_event_icon": "fa-square-up-right", "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", "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": "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_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:", "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>", "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_icon": "fa-vial-circle-check",
"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

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

View File

@@ -515,7 +515,7 @@
"Maintenance_lang_selector_empty": "Sprache wählen", "Maintenance_lang_selector_empty": "Sprache wählen",
"Maintenance_lang_selector_lable": "Sprachauswahl", "Maintenance_lang_selector_lable": "Sprachauswahl",
"Maintenance_lang_selector_text": "Die Änderung findet serverseitig statt, betrifft also alle verwendeten Geräte.", "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_apply": "Übernehmen",
"Maintenance_themeselector_empty": "Skin wählen", "Maintenance_themeselector_empty": "Skin wählen",
"Maintenance_themeselector_lable": "Skin Auswahl", "Maintenance_themeselector_lable": "Skin Auswahl",
@@ -797,6 +797,7 @@
"general_event_title": "", "general_event_title": "",
"go_to_node_event_icon": "", "go_to_node_event_icon": "",
"go_to_node_event_tooltip": "", "go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "", "report_guid": "",
"report_guid_missing": "", "report_guid_missing": "",
"report_select_format": "Format auswählen:", "report_select_format": "Format auswählen:",
@@ -831,4 +832,4 @@
"settings_update_item_warning": "", "settings_update_item_warning": "",
"test_event_icon": "", "test_event_icon": "",
"test_event_tooltip": "Speichere die Änderungen, bevor Sie die Einstellungen testen." "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_empty": "Choose Language",
"Maintenance_lang_selector_lable": "Select 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_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_apply": "Apply",
"Maintenance_themeselector_empty": "Choose a Skin", "Maintenance_themeselector_empty": "Choose a Skin",
"Maintenance_themeselector_lable": "Select Skin", "Maintenance_themeselector_lable": "Select Skin",
@@ -716,6 +716,7 @@
"general_event_title": "Executing an ad-hoc event", "general_event_title": "Executing an ad-hoc event",
"go_to_node_event_icon": "fa-square-up-right", "go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Navigate to the Network page of the given node", "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": "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_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:", "report_select_format": "Select Format:",

View File

@@ -513,7 +513,7 @@
"Maintenance_lang_selector_empty": "Elija un idioma", "Maintenance_lang_selector_empty": "Elija un idioma",
"Maintenance_lang_selector_lable": "Seleccione su 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_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_apply": "Aplicar",
"Maintenance_themeselector_empty": "Elige un tema", "Maintenance_themeselector_empty": "Elige un tema",
"Maintenance_themeselector_lable": "Seleccionar tema", "Maintenance_themeselector_lable": "Seleccionar tema",
@@ -795,6 +795,7 @@
"general_event_title": "Ejecutar un evento ad-hoc", "general_event_title": "Ejecutar un evento ad-hoc",
"go_to_node_event_icon": "fa-square-up-right", "go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Vaya a la página de Red del nodo indicado", "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": "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_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:", "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>", "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_icon": "fa-vial-circle-check",
"test_event_tooltip": "Guarda tus cambios antes de probar nuevos ajustes." "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_empty": "Choix de la langue",
"Maintenance_lang_selector_lable": "Sélectionner une 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_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_apply": "Appliquer",
"Maintenance_themeselector_empty": "Choisir un thème", "Maintenance_themeselector_empty": "Choisir un thème",
"Maintenance_themeselector_lable": "Sélectionner 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", "general_event_title": "Lancement d'un événement sur mesure",
"go_to_node_event_icon": "fa-square-up-right", "go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Aller vers la page Réseau du nœud concerné", "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": "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_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:", "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>", "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_icon": "fa-vial-circle-check",
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage." "test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
} }

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Scegli lingua", "Maintenance_lang_selector_empty": "Scegli lingua",
"Maintenance_lang_selector_lable": "Seleziona 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_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_apply": "Applica",
"Maintenance_themeselector_empty": "Scegli una skin", "Maintenance_themeselector_empty": "Scegli una skin",
"Maintenance_themeselector_lable": "Seleziona skin", "Maintenance_themeselector_lable": "Seleziona skin",
@@ -716,6 +716,7 @@
"general_event_title": "Esecuzione di un evento ad-hoc", "general_event_title": "Esecuzione di un evento ad-hoc",
"go_to_node_event_icon": "fa-square-up-right", "go_to_node_event_icon": "fa-square-up-right",
"go_to_node_event_tooltip": "Passa alla pagina Rete del nodo specificato", "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": "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_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:", "report_select_format": "Seleziona formato:",

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Velg språk", "Maintenance_lang_selector_empty": "Velg språk",
"Maintenance_lang_selector_lable": "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_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_apply": "Bruk",
"Maintenance_themeselector_empty": "Velg ett skinn", "Maintenance_themeselector_empty": "Velg ett skinn",
"Maintenance_themeselector_lable": "Velg skinn", "Maintenance_themeselector_lable": "Velg skinn",
@@ -716,6 +716,7 @@
"general_event_title": "Utfører en ad-hoc hendelse", "general_event_title": "Utfører en ad-hoc hendelse",
"go_to_node_event_icon": "", "go_to_node_event_icon": "",
"go_to_node_event_tooltip": "", "go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "Notifikasjons GUID:", "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_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:", "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>", "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_icon": "fa-vial-circle-check",
"test_event_tooltip": "Lagre endringene først, før du tester innstillingene dine." "test_event_tooltip": "Lagre endringene først, før du tester innstillingene dine."
} }

View File

@@ -716,6 +716,7 @@
"general_event_title": "Wykonywanie wydarzeń ad-hoc", "general_event_title": "Wykonywanie wydarzeń ad-hoc",
"go_to_node_event_icon": "", "go_to_node_event_icon": "",
"go_to_node_event_tooltip": "", "go_to_node_event_tooltip": "",
"new_version_available": "",
"report_guid": "Przewodnik powiadomień:", "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_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:", "report_select_format": "Wybierz Format:",

View File

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

View File

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

View File

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

View File

@@ -489,7 +489,7 @@
"Maintenance_lang_selector_empty": "Виберіть мову", "Maintenance_lang_selector_empty": "Виберіть мову",
"Maintenance_lang_selector_lable": "Виберіть мови", "Maintenance_lang_selector_lable": "Виберіть мови",
"Maintenance_lang_selector_text": "Зміна відбувається на стороні клієнта, тому вона впливає лише на поточний браузер.", "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_apply": "Застосувати",
"Maintenance_themeselector_empty": "Виберіть скін", "Maintenance_themeselector_empty": "Виберіть скін",
"Maintenance_themeselector_lable": "Виберіть Скін", "Maintenance_themeselector_lable": "Виберіть Скін",
@@ -716,6 +716,7 @@
"general_event_title": "Виконання спеціальної події", "general_event_title": "Виконання спеціальної події",
"go_to_node_event_icon": "fa-квадрат-вгору-вправо", "go_to_node_event_icon": "fa-квадрат-вгору-вправо",
"go_to_node_event_tooltip": "Перейдіть на сторінку Мережа даного вузла", "go_to_node_event_tooltip": "Перейдіть на сторінку Мережа даного вузла",
"new_version_available": "Доступна нова версія.",
"report_guid": "Довідник сповіщень:", "report_guid": "Довідник сповіщень:",
"report_guid_missing": "Пов’язане сповіщення не знайдено. Існує невелика затримка між нещодавно надісланими сповіщеннями та їх доступністю. Оновіть сторінку та кеш через кілька секунд. Також можливо, вибране сповіщення було видалено під час обслуговування, як зазначено в параметрі <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Натомість відображається останнє сповіщення. Відсутнє сповіщення має такий GUID:", "report_guid_missing": "Пов’язане сповіщення не знайдено. Існує невелика затримка між нещодавно надісланими сповіщеннями та їх доступністю. Оновіть сторінку та кеш через кілька секунд. Також можливо, вибране сповіщення було видалено під час обслуговування, як зазначено в параметрі <code>DBCLNP_NOTIFI_HIST</code>. <br/> <br/>Натомість відображається останнє сповіщення. Відсутнє сповіщення має такий GUID:",
"report_select_format": "Виберіть формат:", "report_select_format": "Виберіть формат:",

View File

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

View File

@@ -7,20 +7,9 @@
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper integrations-plugins"> <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 <?php
require 'pluginsCore.php'; require 'pluginsCore.php';
?> ?>
</div> </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/) | | `APPRISE` | ▶️ | Apprise notification proxy | | | Script | [_publisher_apprise](/front/plugins/_publisher_apprise/) |
| `ARPSCAN` | 🔍 | ARP-scan on current network | | | Script | [arp_scan](/front/plugins/arp_scan/) | | `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/) | | `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/) | | `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/) | | `DBCLNP` | ⚙ | Database cleanup | | Yes* | Script | [db_cleanup](/front/plugins/db_cleanup/) |
| `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](/front/plugins/ddns_update/) | | `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](/front/plugins/ddns_update/) |
| `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](/front/plugins/dhcp_leases/) | | `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/) | | `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](/front/plugins/internet_ip/) |
| `INTRSPD` | ♻ | Internet speed test | | | Script | [internet_speedtest](/front/plugins/internet_speedtest/) | | `INTRSPD` | ♻ | Internet speed test | | | Script | [internet_speedtest](/front/plugins/internet_speedtest/) |
| `IPNEIGH` | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](/front/plugins/ipneigh/) | | `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/) | | `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/) |
| `MAINT` | ⚙ | Maintenance of logs, etc. | | | Script | [maintenance](/front/plugins/maintenance/) | | `MAINT` | ⚙ | Maintenance of logs, etc. | | | Script | [maintenance](/front/plugins/maintenance/) |
| `MQTT` | ▶️ | MQTT for synching to Home Assistant | | | Script | [_publisher_mqtt](/front/plugins/_publisher_mqtt/) | | `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/) | | `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/) | | `SETPWD` | ⚙ | Set password | | Yes | Template | [set_password](/front/plugins/set_password/) |
| `SMTP` | ▶️ | Email notifications | | | Script | [_publisher_email](/front/plugins/_publisher_email/) | | `SMTP` | ▶️ | Email notifications | | | Script | [_publisher_email](/front/plugins/_publisher_email/) |
| `SNMPDSC` | 🔍/📥 | SNMP device import & sync | | | Script | [snmp_discovery](/front/plugins/snmp_discovery/) | | `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/) | | `TELEGRAM` | ▶️ | Telegram notifications | | | Script | [_publisher_telegram](/front/plugins/_publisher_telegram/) |
| `UI` | ♻ | UI specific settings | | Yes | Template | [ui_settings](/front/plugins/ui_settings/) | | `UI` | ♻ | UI specific settings | | Yes | Template | [ui_settings](/front/plugins/ui_settings/) |
| `UNDIS` | 🔍/📥 | Create dummy devices ❌ | | | Script | [undiscoverables](/front/plugins/undiscoverables/) | | `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 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 > ❌ marked for removal
> ⌚It's recommended to use the same schedule interval for all plugins responsible for discovering new devices. > ⌚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 ### 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

@@ -11,10 +11,12 @@ This Plugin is using awesome [asusrouter](https://github.com/Vaskivskyi/asusrout
### Notes ### Notes
- In case an existing imported device is renamed in Asus Router it will not be renamed in NetAlertX. In this case it has to be done manually or the device should be removed and it will appear on the next scan. - In case an existing imported device is renamed in Asus Router it will not be renamed in NetAlertX. In this case it has to be done manually or the device should be removed and it will appear on the next scan.
- Only clients listed in the main AsusWRT interface are imported. If using plugins, such as the `YazFi plugin`, check the [Asus routers DHCPLSS guide](/front/plugins/dhcp_leases/ASUS_ROUTERS.md) for a possible workaround.
## Other info ## Other info
Version: 1.0.0 - Version: 1.0.0
Date: 16.1.2025 - Author: [labmonkey](https://github.com/labmonkey)
Author: @labmonkey - Release Date: 16.1.2025

View File

@@ -207,7 +207,7 @@
"description": [ "description": [
{ {
"language_code": "en_us", "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", "function": "password",
"type": { "type": {

View File

@@ -77,8 +77,13 @@ def get_device_data():
# Create aiohttp session # Create aiohttp session
session = aiohttp.ClientSession(loop=loop) 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 router = AsusRouter( # required - both IP and URL supported
hostname=get_setting_value("ASUSWRT_host"), # required hostname=get_setting_value("ASUSWRT_host"), # required
port=(None if not port else port), # optional
username=get_setting_value("ASUSWRT_user"), # required username=get_setting_value("ASUSWRT_user"), # required
password=get_setting_value("ASUSWRT_password"), # required password=get_setting_value("ASUSWRT_password"), # required
use_ssl=get_setting_value("ASUSWRT_ssl"), # optional use_ssl=get_setting_value("ASUSWRT_ssl"), # optional
@@ -86,14 +91,9 @@ def get_device_data():
) )
# Connect to the router # Connect to the router
# Throws an error in case of failure
loop.run_until_complete(router.async_connect()) 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 # Now you can use the router object to call methods
clients = loop.run_until_complete(router.async_get_data(AsusData.CLIENTS)) clients = loop.run_until_complete(router.async_get_data(AsusData.CLIENTS))

View File

@@ -0,0 +1,96 @@
# Configuring the `DHCPLSS` plugin to import clients from the YazFi plugin
## Requirements:
1. Only for ASUS routers with the Merlin FW and Entware installed
2. You have guest networks modified with the YazFi pluginwith unidirectional communication from the private network to the guest network configured:
- One way to guest: Yes
## Problem: Clients inaccessible with the Asus API:
- When using YazFi on an ASUS router, the guest clients will no longer be displayed in the regular client list
- The guests are logged in the YazFi plugin and the networks are in an advanced mode
- The `ASUSWRT` plugin by [labmonkey](https://github.com/labmonkey) can only access the clients from the Asus client list but not the guests in the YazFi plugin
## Solution: Getting the `dnsmasq.leases` from the Asus router and configuriong the `DHCPLSS` plugin:
1. Enable SSH login on your Asus router
2. Generate a pair of SSH keys and place them inside `/root/.ssh/`
3. In your router's admin-settings, paste the public key and disable "password login" for SSH
4. On your docker machine, create a script (I placed it in /home/root):
- Replace the IP if necessary.
- Replace `ssh2_privateKey` and `asususer` with your keyfile and your routers login name.
- Replace `/mnt/service-data/netalertx_dhcp.leases/` with your preferred save path inside the docker machine.
`nano grabdnsmasq.sh`
```bash
#!/bin/bash
rsync -avzh -e "ssh -i /root/.ssh/ssh2_privateKey" asususer@192.168.1.1:/var/lib/misc/dnsmasq.leases /mnt/service-data/netalertx_dhcp.leases/
```
5. Create a config file in `/root/.ssh/`:
- Again, replace the IP, the SSH key and the user and also the port if necessary
```
Host ASUS-GT-AXE16000
HostName 192.168.1.1
IdentityFile /root/.ssh/ssh2_privateKey
IdentitiesOnly yes
User asususer
Port 22
```
6. Try a dry run with the command in step 4. If everything is fine, you should have a `dnsmasq.leases` file at your target location
7. Edit crontab for root:
`crontab -e`
add your scheduled time and the path to your script file:
`*/2 * * * * /root/grabdnsmasq.sh`
8. Save and reload the cron service:
`service cron reload`
9. Load the `DHCPLSS` plugin in NetAlertX and add the newly generated dhcp.leases file into the container with a path that must contain the string `dnsmasq`. An example of the mount point could be:
```yaml
volumes:
- /mnt/service-data/netalertx_dhcp.leases:/etc/dnsmasq
...
```
10. Load the `DHCPLSS` plugin and add the search path: `/etc/dnsmasq/dnsmasq.leases`
Configure the plugin, and save everything. You can trigger a manual run.
> [!NOTE]
> DHCP leases don't allow for realtime tracking and the freshness of the data depends on the DHCP leasing time (usually set to 1 or 24h, or 3600 to 86400 seconds).
For a Docker LXC setup the file could be located at `/mnt/service-data/netalertx_dhcp.leases/dnsmasq.leases`.
## Quick setup overview:
```python
DHCPLSS_RUN: 'schedule'
DHCPLSS_CMD: 'python3 /app/front/plugins/dhcp_leases/script.py paths={paths}'
DHCPLSS_paths_to_check: ['/etc/dnsmasq/dnsmasq.leases']
DHCPLSS_RUN_SCHD: '*/5 * * * *'
DHCPLSS_TUN_TIMEOUT: 5
DHCPLSS_WATCH: ['Watched_Value1', 'Watched_Value4']
DHCPLSS_REPORT_ON: ['new', 'watched_changed']
```
You can check the the `dnsmasq.leases` file in the container by running `ls /etc/dnsmasq/`:
```bash
CT_NetAlertX:/# ls /etc/dnsmasq/
dnsmasq.leases
```
## Other Info
- Publishing date: 22.1.2025
- Author: [EinKantHolz - odin](https://github.com/EinKantHolz)

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`. 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) - Version: 1.0
- Date : 2-Dec-2024 - 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 ### Usage
- Check the Settings page for details. - 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 ### Usage
- Check the Settings page for details. - 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) 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: if new_internet_IP == no_internet_ip:
time.sleep(1*i) # Exponential backoff strategy time.sleep(1*i) # Exponential backoff strategy
else: 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 - `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 - 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) - Version: 1.0
- Date : 31-Nov-2024 - 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 ### Other info
- Author : [vaga9938](https://github.com/vaga9938) - Version: 1.0
- Date : 28-Dec-2024 - 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) - 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 multiprocessing
# import netifaces
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])

View File

@@ -146,7 +146,7 @@
"description": [ "description": [
{ {
"language_code": "en_us", "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": [ "description": [
{ {
"language_code": "en_us", "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 ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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 ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">

View File

@@ -1,31 +1,14 @@
<?php <?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/header.php';
require 'php/templates/notification.php';
?> ?>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
<!-- Content header--------------------------------------------------------- --> <!-- 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 ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content tab-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"> <div id="settingsPage" class="content-wrapper">
<!-- Content header--------------------------------------------------------- --> <!-- 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"> <section class="content-header">
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" > <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"> <a data-toggle="collapse" href="#settingsOverview">
<div class ="settings-group col-sm-12 panel-heading panel-title"> <div class ="settings-group col-sm-12 panel-heading panel-title">
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?> <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"> <section class=" padding-bottom col-sm-12">
<!-- needed so the filter & save button don't hide the settings --> <!-- 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> </section>

View File

@@ -13,21 +13,13 @@
require 'php/templates/header.php'; require 'php/templates/header.php';
?> ?>
<?php require 'php/templates/notification.php'; ?>
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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 ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">

View File

@@ -11,18 +11,6 @@ require 'php/templates/header.php';
<div id="notifications" class="content-wrapper"> <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"> <section class="content">
<div class="box box-gray col-xs-12" > <div class="box box-gray col-xs-12" >
<div class="box-header"> <div class="box-header">

View File

@@ -9,16 +9,6 @@
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <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 <?php
require 'appEventsCore.php'; require 'appEventsCore.php';
?> ?>

View File

@@ -1,5 +1,2 @@
# Schedule cron jobs # Schedule cron jobs
* * * * * /app/back/cron_script.sh * * * * * /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 update-alternatives --install /usr/bin/python python /usr/bin/python3 10
# install packages thru pip3 # 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 ### Other info
- Date : 08-Jan-2025 - version 1.0 - Version: 1.0
- Author: N/A - Author: N/A
- Release Date: 08-Jan-2025
> [!NOTE] > [!NOTE]
> This is a community supplied script and not maintained. > 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 ### Other info
- Date : 23-Dec-2024 - version 1.0 - Version: 1.0
- Release Date: 23-Dec-2024
- Author: [laxduke](https://github.com/laxduke) - Author: [laxduke](https://github.com/laxduke)

View File

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

View File

@@ -7,8 +7,9 @@ import datetime
import conf 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 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 logger import mylog
from helper import write_file, get_setting_value, updateState, timeNowTZ from helper import write_file, get_setting_value, timeNowTZ
from execution_log import ExecutionLog from app_state import updateState
from user_events_queue import UserEventsQueue
from notification import write_notification from notification import write_notification
# Import the start_server function # 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) start_periodic_write(interval=1)
# Update app_state.json and retrieve app_state to check if GraphQL server is running # 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 # 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 # Prepare database tables we want to expose
dataSourcesSQLs = [ dataSourcesSQLs = [
@@ -57,7 +56,7 @@ def update_api(db, all_plugins, forceUpdate, updateOnlyDataSources=[], is_ad_hoc
# Save selected database tables # Save selected database tables
for dsSQL in dataSourcesSQLs: for dsSQL in dataSourcesSQLs:
if not updateOnlyDataSources or dsSQL[0] in updateOnlyDataSources: 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 # Start the GraphQL server
graphql_port_value = get_setting_value("GRAPHQL_PORT") graphql_port_value = get_setting_value("GRAPHQL_PORT")
@@ -87,7 +86,7 @@ class api_endpoint_class:
self.path = path self.path = path
self.fileName = path.split('/')[-1] self.fileName = path.split('/')[-1]
self.hash = hash(json.dumps(self.jsonData)) 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.changeDetectedWhen = None
# self.last_update_time = current_time - datetime.timedelta(minutes=1) # Last time data was updated # 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 self.is_ad_hoc_user_event = is_ad_hoc_user_event
@@ -147,7 +146,7 @@ class api_endpoint_class:
# Update user event execution log # Update user event execution log
# mylog('verbose', [f'[API] api_endpoint_class: is_ad_hoc_user_event {self.is_ad_hoc_user_event}']) # 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: if self.is_ad_hoc_user_event:
execution_log = ExecutionLog() execution_log = UserEventsQueue()
execution_log.finalize_event("update_api") execution_log.finalize_event("update_api")
self.is_ad_hoc_user_event = False 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 const import fullDbPath, sql_devices_stats, sql_devices_all, sql_generateGuid
from logger import mylog 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 from appevent import AppEvent_obj
class DB(): class DB():

View File

@@ -9,7 +9,8 @@ INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/server"])
from logger import mylog 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 from notification import write_notification
# Flask application # Flask application

View File

@@ -52,94 +52,6 @@ def get_timezone_offset():
return offset_formatted 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): def updateSubnets(scan_subnets):
subnets = [] subnets = []
@@ -887,14 +799,6 @@ def add_json_list (row, list):
return 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. # 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 # Register NetAlertX libraries
import conf import conf
from const import fullConfPath, applicationPath, fullConfFolder 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 logger import mylog
from api import update_api from api import update_api
from scheduler import schedule_class from scheduler import schedule_class
@@ -133,7 +134,7 @@ def importConfigs (db, all_plugins):
if (fileModifiedTime == conf.lastImportedConfFile) and all_plugins is not None: if (fileModifiedTime == conf.lastImportedConfFile) and all_plugins is not None:
mylog('debug', ['[Import Config] skipping config file import']) mylog('debug', ['[Import Config] skipping config file import'])
return all_plugins return all_plugins, False
# Header # Header
updateState("Import config", showSpinner = True) updateState("Import config", showSpinner = True)
@@ -311,8 +312,7 @@ def importConfigs (db, all_plugins):
# ----------------- # -----------------
# HANDLE APP_CONF_OVERRIDE via app_conf_override.json # HANDLE APP_CONF_OVERRIDE via app_conf_override.json
# Assuming fullConfFolder is defined elsewhere
app_conf_override_path = fullConfFolder + '/app_conf_override.json' app_conf_override_path = fullConfFolder + '/app_conf_override.json'
if os.path.exists(app_conf_override_path): if os.path.exists(app_conf_override_path):
@@ -413,7 +413,7 @@ def importConfigs (db, all_plugins):
# front end app log loggging # front end app log loggging
write_notification(msg, 'info', timeNowTZ()) write_notification(msg, 'info', timeNowTZ())
return all_plugins return all_plugins, True

View File

@@ -4,6 +4,10 @@ import datetime
import threading import threading
import queue import queue
import time import time
import logging
# NetAlertX imports
import conf import conf
from const import * from const import *
@@ -16,6 +20,16 @@ def timeNowTZ():
else: else:
return datetime.datetime.now().replace(microsecond=0) return datetime.datetime.now().replace(microsecond=0)
#-------------------------------------------------------------------------------
# Map custom debug levels to Python logging levels
custom_to_logging_levels = {
'none': logging.NOTSET,
'minimal': logging.WARNING,
'verbose': logging.INFO,
'debug': logging.DEBUG,
'trace': logging.DEBUG, # Can map to DEBUG or lower custom level if needed
}
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# More verbose as the numbers go up # More verbose as the numbers go up
debugLevels = [ debugLevels = [
@@ -25,6 +39,10 @@ debugLevels = [
# use the LOG_LEVEL from the config, may be overridden # use the LOG_LEVEL from the config, may be overridden
currentLevel = conf.LOG_LEVEL currentLevel = conf.LOG_LEVEL
# tracking log levels
setLvl = 0
reqLvl = 0
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
class Logger: class Logger:
def __init__(self, LOG_LEVEL='verbose'): def __init__(self, LOG_LEVEL='verbose'):
@@ -32,10 +50,36 @@ class Logger:
currentLevel = LOG_LEVEL currentLevel = LOG_LEVEL
# Automatically set up custom logging handler
self.setup_logging()
def setup_logging(self):
root_logger = logging.getLogger()
# Clear existing handlers to prevent duplicates
if root_logger.hasHandlers():
root_logger.handlers.clear()
# Create the custom handler
my_log_handler = MyLogHandler()
# my_log_handler.setLevel(custom_to_logging_levels.get(currentLevel, logging.NOTSET))
# Optional: Add a formatter for consistent log message format
# formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter = logging.Formatter('%(message)s', datefmt='%H:%M:%S')
my_log_handler.setFormatter(formatter)
# Attach the handler to the root logger
root_logger.addHandler(my_log_handler)
root_logger.setLevel(custom_to_logging_levels.get(currentLevel, logging.NOTSET))
# for python logging
class MyLogHandler(logging.Handler):
def emit(self, record):
log_entry = self.format(record)
log_queue.put(log_entry)
def mylog(requestedDebugLevel, n): def mylog(requestedDebugLevel, n):
setLvl = 0 global setLvl, reqLvl
reqLvl = 0
# Get debug urgency/relative weight # Get debug urgency/relative weight
for lvl in debugLevels: for lvl in debugLevels:
@@ -86,23 +130,14 @@ def file_print(*args):
result = timeNowTZ().strftime('%H:%M:%S') + ' ' result = timeNowTZ().strftime('%H:%M:%S') + ' '
for arg in args: for arg in args:
result += str(arg) result += str(arg)
logging.log(custom_to_logging_levels.get(currentLevel, logging.NOTSET), result) # Forward to Python's logging system
print(result) print(result)
# Ensure the log writer thread is running # Ensure the log writer thread is running
start_log_writer_thread() start_log_writer_thread()
# Queue the log entry for writing
append_to_file_with_timeout( result, 5)
#-------------------------------------------------------------------------------
# Function to append to the file with a timeout
def append_to_file_with_timeout(data, timeout):
try:
log_queue.put_nowait(data)
except queue.Full:
print("Log queue is full, dropping log entry:" + data)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def print_log(pText): def print_log(pText):
# Check if logging is active # Check if logging is active

View File

@@ -12,11 +12,12 @@ from collections import namedtuple
import conf import conf
from const import pluginsPath, logPath, applicationPath, reportTemplatesPath from const import pluginsPath, logPath, applicationPath, reportTemplatesPath
from logger import mylog, Logger 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 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 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 notification import Notification_obj, write_notification
from execution_log import ExecutionLog from user_events_queue import UserEventsQueue
# Make sure log level is initialized correctly # Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL')) Logger(get_setting_value('LOG_LEVEL'))
@@ -102,12 +103,7 @@ class plugin_param:
self.multiplyTimeout = multiplyTimeout self.multiplyTimeout = multiplyTimeout
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
class plugins_state: def run_plugin_scripts(db, all_plugins, runType):
def __init__(self, processScan = False):
self.processScan = processScan
#-------------------------------------------------------------------------------
def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state()):
# Header # Header
updateState("Run: Plugins") updateState("Run: Plugins")
@@ -140,7 +136,7 @@ def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state())
print_plugin_info(plugin, ['display_name']) print_plugin_info(plugin, ['display_name'])
mylog('debug', ['[Plugins] CMD: ', get_plugin_setting_obj(plugin, "CMD")["value"]]) 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 # update last run time
if runType == "schedule": if runType == "schedule":
for schd in conf.mySchedules: 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 # note the last time the scheduled plugin run was executed
schd.last_run = timeNowTZ() schd.last_run = timeNowTZ()
return pluginsState
# Function to run a plugin command # Function to run a plugin command
def run_plugin(command, set_RUN_TIMEOUT, plugin): 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 # 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 sql = db.sql
if pluginsState is None:
mylog('debug', ['[Plugins] pluginsState is None'])
pluginsState = plugins_state()
# ------- necessary settings check -------- # ------- necessary settings check --------
set = get_plugin_setting_obj(plugin, "CMD") set = get_plugin_setting_obj(plugin, "CMD")
# handle missing "function":"CMD" setting # handle missing "function":"CMD" setting
if set == None: if set == None:
return pluginsState return
set_CMD = set["value"] set_CMD = set["value"]
@@ -394,7 +382,7 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
# handle missing "function":"DB_PATH" setting # handle missing "function":"DB_PATH" setting
if set == None: if set == None:
mylog('none', ['[Plugins] ⚠ ERROR: DB_PATH setting for plugin type sqlite-db-query missing.']) mylog('none', ['[Plugins] ⚠ ERROR: DB_PATH setting for plugin type sqlite-db-query missing.'])
return pluginsState return
fullSqlitePath = set["value"] fullSqlitePath = set["value"]
@@ -408,7 +396,7 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
except sqlite3.Error as e: 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: 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]) mylog('none',[f'[Plugins] ⚠ ERROR: ATTACH DATABASE failed with SQL ERROR: ', e])
return pluginsState return
for row in arr: for row in arr:
# There has to be always 9 or 13 columns # 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 # check if the subprocess / SQL query failed / there was no valid output
if len(sqlParams) == 0: if len(sqlParams) == 0:
mylog('none', [f'[Plugins] No output received from the plugin "{plugin["unique_prefix"]}"']) mylog('none', [f'[Plugins] No output received from the plugin "{plugin["unique_prefix"]}"'])
return pluginsState return
else: else:
mylog('verbose', ['[Plugins] SUCCESS, received ', len(sqlParams), ' entries']) mylog('verbose', ['[Plugins] SUCCESS, received ', len(sqlParams), ' entries'])
mylog('debug', ['[Plugins] sqlParam entries: ', sqlParams]) mylog('debug', ['[Plugins] sqlParam entries: ', sqlParams])
@@ -468,17 +456,24 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
if len(sqlParams) > 0: if len(sqlParams) > 0:
# create objects # create objects
pluginsState = process_plugin_events(db, plugin, pluginsState, sqlParams) process_plugin_events(db, plugin, sqlParams)
# update API endpoints # 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 # 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 sql = db.sql
@@ -780,13 +775,14 @@ def process_plugin_events(db, plugin, pluginsState, plugEventsArr):
db.commitDB() db.commitDB()
# perform scan if mapped to CurrentScan table # perform scan if mapped to CurrentScan table
if dbTable == 'CurrentScan': if dbTable == 'CurrentScan':
pluginsState.processScan = True updateState("Process scan: True", None, None, None, None, True) # set processScan = True in the appState
db.commitDB() db.commitDB()
return pluginsState return
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -844,14 +840,15 @@ class plugin_object_class:
self.watchedHash = str(hash(tmp)) self.watchedHash = str(hash(tmp))
#=============================================================================== #===============================================================================
# Handling of user initialized front-end events # 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. 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 # Track whether to show notification for executed events
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 # Read the log file to get the lines
lines = execution_log.read_log() lines = execution_log.read_log()
if not lines: 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: for line in lines:
# Extract event name and parameters from the log line # 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 # Process each event type
if event == 'test': 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}") executed_events.append(f"test with param {param}")
execution_log.finalize_event("test") execution_log.finalize_event("test")
elif event == 'run': 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}") executed_events.append(f"run with param {param}")
execution_log.finalize_event("run") execution_log.finalize_event("run")
elif event == 'update_api': 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]) 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()) 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]) mylog('minimal', ['[', timeNowTZ(), '] START Run: ', runType])
# run the plugin to run # run the plugin to run
for plugin in all_plugins: for plugin in all_plugins:
if plugin["unique_prefix"] == runType: 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]) 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]) mylog('minimal', ['[', timeNowTZ(), '] [Test] START Test: ', runType])
@@ -924,12 +924,12 @@ def handle_test(runType, db, all_plugins, pluginsState):
notificationObj = notification.create(sample_json, "") notificationObj = notification.create(sample_json, "")
# Run test # Run test
pluginsState = handle_run(runType, db, all_plugins, pluginsState) handle_run(runType, db, all_plugins)
# Remove sample notification # Remove sample notification
notificationObj.remove(notificationObj.GUID) notificationObj.remove(notificationObj.GUID)
mylog('minimal', ['[Test] END Test: ', runType]) mylog('minimal', ['[Test] END Test: ', runType])
return pluginsState return

View File

@@ -4,7 +4,8 @@ import json
import conf import conf
from logger import mylog from logger import mylog
from const import pluginsPath, logPath, apiPath 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 from crypto_utils import decrypt_data
module_name = 'Plugin utils' module_name = 'Plugin utils'

View File

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