mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
16 Commits
v24.1.19
...
c63f424c7d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c63f424c7d | ||
|
|
dd1580e536 | ||
|
|
630e4f6327 | ||
|
|
cb8af32553 | ||
|
|
d469a9ded4 | ||
|
|
c8a40920b4 | ||
|
|
b0cd9acb79 | ||
|
|
6129f31a24 | ||
|
|
3eb8f39b5c | ||
|
|
5b1002620b | ||
|
|
e50d757f57 | ||
|
|
5110a3c2f3 | ||
|
|
abf7be5958 | ||
|
|
82708bd5df | ||
|
|
b5dce3f6aa | ||
|
|
5562ae7add |
6
.github/workflows/social_post_on_release.yml
vendored
6
.github/workflows/social_post_on_release.yml
vendored
@@ -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! 🚀
|
||||||
|
|||||||
28
Dockerfile
28
Dockerfile
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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';
|
|
||||||
?>
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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": "",
|
||||||
|
|||||||
@@ -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ó."
|
||||||
}
|
}
|
||||||
@@ -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": "",
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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:",
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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:",
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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:",
|
||||||
|
|||||||
@@ -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": ""
|
||||||
}
|
}
|
||||||
@@ -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": "Сначала сохраните изменения, прежде чем проверять настройки."
|
||||||
}
|
}
|
||||||
@@ -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": "",
|
||||||
|
|||||||
@@ -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": "Виберіть формат:",
|
||||||
|
|||||||
@@ -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": "在测试设置之前,请先保存更改。"
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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>`
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
|
|||||||
96
front/plugins/dhcp_leases/ASUS_ROUTERS.md
Executable file
96
front/plugins/dhcp_leases/ASUS_ROUTERS.md
Executable 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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"])
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -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
15
install/freebox_certificate.pem
Executable 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-----
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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'])
|
||||||
|
|
||||||
|
|||||||
@@ -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
109
server/app_state.py
Executable 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)
|
||||||
@@ -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():
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
Reference in New Issue
Block a user