Compare commits
55 Commits
067336dcc1
...
mcp-endpoi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f25560c8 | ||
|
|
fc4d32ebe7 | ||
|
|
b47325d06a | ||
|
|
436ac6de49 | ||
|
|
c1bd611e57 | ||
|
|
edde2596b5 | ||
|
|
da9d37c718 | ||
|
|
5bcb727305 | ||
|
|
2dc688b16c | ||
|
|
0ac9fd79b3 | ||
|
|
3d17dc47b5 | ||
|
|
ef2e7886c4 | ||
|
|
c8f3a84b92 | ||
|
|
9688fee2d2 | ||
|
|
2dcd9eda19 | ||
|
|
24187495e1 | ||
|
|
c27d25d4ab | ||
|
|
93a2dad2eb | ||
|
|
b235863644 | ||
|
|
f387f8c5b6 | ||
|
|
5af760f5ee | ||
|
|
d93a3981fa | ||
|
|
fbb4a2f8b4 | ||
|
|
54bce6505b | ||
|
|
6da47cc830 | ||
|
|
9cabbf3622 | ||
|
|
6c28a08bee | ||
|
|
86e3decd4e | ||
|
|
e14e0bb9e8 | ||
|
|
b6023d1373 | ||
|
|
1812cc8ef8 | ||
|
|
5df39f984a | ||
|
|
d007ed711a | ||
|
|
61824abb9f | ||
|
|
33c5548fe1 | ||
|
|
fd41c395ae | ||
|
|
1a980844f0 | ||
|
|
82e018e284 | ||
|
|
e0e1233b1c | ||
|
|
74677f940e | ||
|
|
21a4d20579 | ||
|
|
9634e4e0f7 | ||
|
|
00a47ab5d3 | ||
|
|
59b417705e | ||
|
|
525d082f3d | ||
|
|
ba3481759b | ||
|
|
7125cea29b | ||
|
|
8586c5a307 | ||
|
|
0d81315809 | ||
|
|
8f193f1e2c | ||
|
|
b1eef8aa09 | ||
|
|
2da17f272c | ||
|
|
7bcb4586b2 | ||
|
|
d3326b3362 | ||
|
|
b9d3f430fe |
6
.github/workflows/docker_dev.yml
vendored
@@ -47,6 +47,12 @@ jobs:
|
|||||||
id: get_version
|
id: get_version
|
||||||
run: echo "version=Dev" >> $GITHUB_OUTPUT
|
run: echo "version=Dev" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# --- debug output
|
||||||
|
- name: Debug version
|
||||||
|
run: |
|
||||||
|
echo "GITHUB_REF: $GITHUB_REF"
|
||||||
|
echo "Version: '${{ steps.get_version.outputs.version }}'"
|
||||||
|
|
||||||
# --- Write the timestamped version to .VERSION file
|
# --- Write the timestamped version to .VERSION file
|
||||||
- name: Create .VERSION file
|
- name: Create .VERSION file
|
||||||
run: echo "${{ steps.timestamp.outputs.version }}" > .VERSION
|
run: echo "${{ steps.timestamp.outputs.version }}" > .VERSION
|
||||||
|
|||||||
22
.github/workflows/docker_prod.yml
vendored
@@ -32,14 +32,34 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
# --- Previous approach Get release version from tag
|
||||||
|
- name: Set up dynamic build ARGs
|
||||||
|
id: getargs
|
||||||
|
run: echo "version=$(cat ./stable/VERSION)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Get release version
|
||||||
|
id: get_version_prev
|
||||||
|
run: echo "::set-output name=version::${GITHUB_REF#refs/tags/}"
|
||||||
|
|
||||||
|
- name: Create .VERSION file
|
||||||
|
run: echo "${{ steps.get_version.outputs.version }}" >> .VERSION_PREV
|
||||||
|
|
||||||
# --- Get release version from tag
|
# --- Get release version from tag
|
||||||
- name: Get release version
|
- name: Get release version
|
||||||
id: get_version
|
id: get_version
|
||||||
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
|
# --- debug output
|
||||||
|
- name: Debug version
|
||||||
|
run: |
|
||||||
|
echo "GITHUB_REF: $GITHUB_REF"
|
||||||
|
echo "Version: '${{ steps.get_version.outputs.version }}'"
|
||||||
|
echo "Version prev: '${{ steps.get_version_prev.outputs.version }}'"
|
||||||
|
|
||||||
# --- Write version to .VERSION file
|
# --- Write version to .VERSION file
|
||||||
- name: Create .VERSION file
|
- name: Create .VERSION file
|
||||||
run: echo "${{ steps.get_version.outputs.version }}" > .VERSION
|
run: echo -n "${{ steps.get_version.outputs.version }}" > .VERSION
|
||||||
|
|
||||||
# --- Generate Docker metadata and tags
|
# --- Generate Docker metadata and tags
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -11,6 +11,7 @@ nohup.out
|
|||||||
config/*
|
config/*
|
||||||
.ash_history
|
.ash_history
|
||||||
.VERSION
|
.VERSION
|
||||||
|
.VERSION_PREV
|
||||||
config/pialert.conf
|
config/pialert.conf
|
||||||
config/app.conf
|
config/app.conf
|
||||||
db/*
|
db/*
|
||||||
|
|||||||
13
Dockerfile
@@ -138,6 +138,7 @@ RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FO
|
|||||||
|
|
||||||
# Copy version information into the image
|
# Copy version information into the image
|
||||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} .[V]ERSION ${NETALERTX_APP}/.VERSION
|
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} .[V]ERSION ${NETALERTX_APP}/.VERSION
|
||||||
|
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} .[V]ERSION ${NETALERTX_APP}/.VERSION_PREV
|
||||||
|
|
||||||
# Copy the virtualenv from the builder stage
|
# Copy the virtualenv from the builder stage
|
||||||
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||||
@@ -147,12 +148,12 @@ COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
|||||||
# This is done after the copy of the venv to ensure the venv is in place
|
# This is done after the copy of the venv to ensure the venv is in place
|
||||||
# although it may be quicker to do it before the copy, it keeps the image
|
# although it may be quicker to do it before the copy, it keeps the image
|
||||||
# layers smaller to do it after.
|
# layers smaller to do it after.
|
||||||
RUN if [ -f '.VERSION' ]; then \
|
RUN for vfile in .VERSION .VERSION_PREV; do \
|
||||||
cp '.VERSION' "${NETALERTX_APP}/.VERSION"; \
|
if [ ! -f "${NETALERTX_APP}/${vfile}" ]; then \
|
||||||
else \
|
echo "DEVELOPMENT 00000000" > "${NETALERTX_APP}/${vfile}"; \
|
||||||
echo "DEVELOPMENT 00000000" > "${NETALERTX_APP}/.VERSION"; \
|
fi; \
|
||||||
fi && \
|
chown 20212:20212 "${NETALERTX_APP}/${vfile}"; \
|
||||||
chown 20212:20212 "${NETALERTX_APP}/.VERSION" && \
|
done && \
|
||||||
apk add --no-cache libcap && \
|
apk add --no-cache libcap && \
|
||||||
setcap cap_net_raw+ep /bin/busybox && \
|
setcap cap_net_raw+ep /bin/busybox && \
|
||||||
setcap cap_net_raw,cap_net_admin+eip /usr/bin/nmap && \
|
setcap cap_net_raw,cap_net_admin+eip /usr/bin/nmap && \
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ Get visibility of what's going on on your WIFI/LAN network and enable presence d
|
|||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> ⚠️ **Important:** The documentation has been recently updated and some instructions may have changed.
|
> ⚠️ **Important:** The docker-compose has recently changed. Carefully read the [Migration guide](https://jokob-sk.github.io/NetAlertX/MIGRATION/?h=migrat#12-migration-from-netalertx-v25524) for detailed instructions.
|
||||||
> If you are using the currently live production image, please follow the instructions on [Docker Hub](https://hub.docker.com/r/jokobsk/netalertx) for building and running the container.
|
|
||||||
> These docs reflect the latest development version and may differ from the production image.
|
|
||||||
|
|
||||||
Start NetAlertX in seconds with Docker:
|
Start NetAlertX in seconds with Docker:
|
||||||
|
|
||||||
@@ -44,8 +42,7 @@ Start NetAlertX in seconds with Docker:
|
|||||||
docker run -d \
|
docker run -d \
|
||||||
--network=host \
|
--network=host \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
-v /local_data_dir/config:/data/config \
|
-v /local_data_dir:/data \
|
||||||
-v /local_data_dir/db:/data/db \
|
|
||||||
-v /etc/localtime:/etc/localtime:ro \
|
-v /etc/localtime:/etc/localtime:ro \
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
@@ -53,6 +50,8 @@ docker run -d \
|
|||||||
ghcr.io/jokob-sk/netalertx:latest
|
ghcr.io/jokob-sk/netalertx:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||||
|
|
||||||
To deploy a containerized instance directly from the source repository, execute the following BASH sequence:
|
To deploy a containerized instance directly from the source repository, execute the following BASH sequence:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/jokob-sk/NetAlertX.git
|
git clone https://github.com/jokob-sk/NetAlertX.git
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# NetAlertX API Documentation
|
# API Documentation
|
||||||
|
|
||||||
This API provides programmatic access to **devices, events, sessions, metrics, network tools, and sync** in NetAlertX. It is implemented as a **REST and GraphQL server**. All requests require authentication via **API Token** (`API_TOKEN` setting) unless explicitly noted. For example, to authorize a GraphQL request, you need to use a `Authorization: Bearer API_TOKEN` header as per example below:
|
This API provides programmatic access to **devices, events, sessions, metrics, network tools, and sync** in NetAlertX. It is implemented as a **REST and GraphQL server**. All requests require authentication via **API Token** (`API_TOKEN` setting) unless explicitly noted. For example, to authorize a GraphQL request, you need to use a `Authorization: Bearer API_TOKEN` header as per example below:
|
||||||
|
|
||||||
|
|||||||
@@ -112,3 +112,11 @@ Slowness can be caused by:
|
|||||||
|
|
||||||
> See [Performance Tips](./PERFORMANCE.md) for detailed optimization steps.
|
> See [Performance Tips](./PERFORMANCE.md) for detailed optimization steps.
|
||||||
|
|
||||||
|
|
||||||
|
#### IP flipping
|
||||||
|
|
||||||
|
With `ARPSCAN` scans some devices might flip IP addresses after each scan triggering false notifications. This is because some devices respond to broadcast calls and thus different IPs after scans are logged.
|
||||||
|
|
||||||
|
See how to prevent IP flipping in the [ARPSCAN plugin guide](/front/plugins/arp_scan/README.md).
|
||||||
|
|
||||||
|
Alternatively adjust your [notification settings](./NOTIFICATIONS.md) to prevent false positives by filtering out events or devices.
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ Start the container via the **terminal** with a command similar to this one:
|
|||||||
docker run \
|
docker run \
|
||||||
--network=host \
|
--network=host \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
-v /local_data_dir/config:/data/config \
|
-v /local_data_dir:/data \
|
||||||
-v /local_data_dir/db:/data/db \
|
|
||||||
-v /etc/localtime:/etc/localtime:ro \
|
-v /etc/localtime:/etc/localtime:ro \
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
@@ -26,6 +25,8 @@ docker run \
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> ⚠ The most important part is NOT to use the `-d` parameter so you see the error when the container crashes. Use this error in your issue description.
|
> ⚠ The most important part is NOT to use the `-d` parameter so you see the error when the container crashes. Use this error in your issue description.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# NetAlertX - Device Management
|
# Device Management
|
||||||
|
|
||||||
The Main Info section is where most of the device identifiable information is stored and edited. Some of the information is autodetected via various plugins. Initial values for most of the fields can be specified in the `NEWDEV` plugin.
|
The Main Info section is where most of the device identifiable information is stored and edited. Some of the information is autodetected via various plugins. Initial values for most of the fields can be specified in the `NEWDEV` plugin.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
# NetAlertX and Docker Compose
|
# NetAlertX and Docker Compose
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> ⚠️ **Important:** The documentation has been recently updated and some instructions may have changed.
|
> ⚠️ **Important:** The docker-compose has recently changed. Carefully read the [Migration guide](https://jokob-sk.github.io/NetAlertX/MIGRATION/?h=migrat#12-migration-from-netalertx-v25524) for detailed instructions.
|
||||||
> If you are using the currently live production image, please follow the instructions on [Docker Hub](https://hub.docker.com/r/jokobsk/netalertx) for building and running the container.
|
|
||||||
> These docs reflect the latest development version and may differ from the production image.
|
|
||||||
|
|
||||||
Great care is taken to ensure NetAlertX meets the needs of everyone while being flexible enough for anyone. This document outlines how you can configure your docker-compose. There are many settings, so we recommend using the Baseline Docker Compose as-is, or modifying it for your system.Good care is taken to ensure NetAlertX meets the needs of everyone while being flexible enough for anyone. This document outlines how you can configure your docker-compose. There are many settings, so we recommend using the Baseline Docker Compose as-is, or modifying it for your system.
|
Great care is taken to ensure NetAlertX meets the needs of everyone while being flexible enough for anyone. This document outlines how you can configure your docker-compose. There are many settings, so we recommend using the Baseline Docker Compose as-is, or modifying it for your system.Good care is taken to ensure NetAlertX meets the needs of everyone while being flexible enough for anyone. This document outlines how you can configure your docker-compose. There are many settings, so we recommend using the Baseline Docker Compose as-is, or modifying it for your system.
|
||||||
|
|
||||||
@@ -125,9 +123,9 @@ docker compose up
|
|||||||
|
|
||||||
### Modification 1: Use a Local Folder (Bind Mount)
|
### Modification 1: Use a Local Folder (Bind Mount)
|
||||||
|
|
||||||
By default, the baseline compose file uses a single named volume (netalertx_data) mounted at /data. This single-volume layout is preferred because NetAlertX manages both configuration and the database under /data (for example, /data/config and /data/db) via its web UI. Using one named volume simplifies permissions and portability: Docker manages the storage and NetAlertX manages the files inside /data.
|
By default, the baseline compose file uses a single named volume (netalertx_data) mounted at `/data`. This single-volume layout is preferred because NetAlertX manages both configuration and the database under `/data` (for example, `/data/config` and `/data/db`) via its web UI. Using one named volume simplifies permissions and portability: Docker manages the storage and NetAlertX manages the files inside `/data`.
|
||||||
|
|
||||||
A two-volume layout that mounts /data/config and /data/db separately (for example, netalertx_config and netalertx_db) is supported for backward compatibility and some advanced workflows, but it is an abnormal/legacy layout and not recommended for new deployments.
|
A two-volume layout that mounts `/data/config` and `/data/db` separately (for example, `netalertx_config` and `netalertx_db`) is supported for backward compatibility and some advanced workflows, but it is an abnormal/legacy layout and not recommended for new deployments.
|
||||||
|
|
||||||
However, if you prefer to have direct, file-level access to your configuration for manual editing, a "bind mount" is a simple alternative. This tells Docker to use a specific folder from your computer (the "host") inside the container.
|
However, if you prefer to have direct, file-level access to your configuration for manual editing, a "bind mount" is a simple alternative. This tells Docker to use a specific folder from your computer (the "host") inside the container.
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --rm --network=host \
|
docker run -d --rm --network=host \
|
||||||
-v /local_data_dir/config:/data/config \
|
-v /local_data_dir:/data \
|
||||||
-v /local_data_dir/db:/data/db \
|
|
||||||
-v /etc/localtime:/etc/localtime \
|
-v /etc/localtime:/etc/localtime \
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
@@ -62,21 +61,38 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
|
|||||||
|
|
||||||
| Required | Path | Description |
|
| Required | Path | Description |
|
||||||
| :------------- | :------------- | :-------------|
|
| :------------- | :------------- | :-------------|
|
||||||
| ✅ | `:/data/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files |
|
| ✅ | `:/data` | Folder which needs to contain a `/db` and `/config` sub-folders. |
|
||||||
| ✅ | `:/data/db` | Folder which will contain the `app.db` database file |
|
| ✅ | `/etc/localtime:/etc/localtime:ro` | Ensuring the timezone is the same as on the server. |
|
||||||
| ✅ | `/etc/localtime:/etc/localtime:ro` | Ensuring the timezone is teh same as on teh server. |
|
|
||||||
| | `:/tmp/log` | Logs folder useful for debugging if you have issues setting up the container |
|
| | `:/tmp/log` | Logs folder useful for debugging if you have issues setting up the container |
|
||||||
| | `:/tmp/api` | The [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. Path configurable via `NETALERTX_API` environment variable. |
|
| | `:/tmp/api` | The [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. Path configurable via `NETALERTX_API` environment variable. |
|
||||||
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). |
|
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). |
|
||||||
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
|
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
|
||||||
|
|
||||||
> Use separate `db` and `config` directories, do not nest them.
|
### Folder structure
|
||||||
|
|
||||||
|
Use separate `db` and `config` directories, do not nest them:
|
||||||
|
|
||||||
|
```
|
||||||
|
data
|
||||||
|
├── config
|
||||||
|
└── db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permissions
|
||||||
|
|
||||||
|
If you are facing permissions issues run the following commands on your server. This will change the owner and assure sufficient access to the database and config files that are stored in the `/local_data_dir/db` and `/local_data_dir/config` folders (replace `local_data_dir` with the location where your `/db` and `/config` folders are located).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown -R 20211:20211 /local_data_dir
|
||||||
|
sudo chmod -R a+rwx /local_data_dir
|
||||||
|
```
|
||||||
|
|
||||||
### Initial setup
|
### Initial setup
|
||||||
|
|
||||||
- If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
|
- If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
|
||||||
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/data/config/` folder directly
|
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/data/config/` folder directly
|
||||||
|
|
||||||
|
|
||||||
#### Setting up scanners
|
#### Setting up scanners
|
||||||
|
|
||||||
You have to specify which network(s) should be scanned. This is done by entering subnets that are accessible from the host. If you use the default `ARPSCAN` plugin, you have to specify at least one valid subnet and interface in the `SCAN_SUBNETS` setting. See the documentation on [How to set up multiple SUBNETS, VLANs and what are limitations](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for troubleshooting and more advanced scenarios.
|
You have to specify which network(s) should be scanned. This is done by entering subnets that are accessible from the host. If you use the default `ARPSCAN` plugin, you have to specify at least one valid subnet and interface in the `SCAN_SUBNETS` setting. See the documentation on [How to set up multiple SUBNETS, VLANs and what are limitations](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for troubleshooting and more advanced scenarios.
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
# The NetAlertX Container Operator's Guide
|
# The NetAlertX Container Operator's Guide
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> ⚠️ **Important:** The documentation has been recently updated and some instructions may have changed.
|
> ⚠️ **Important:** The docker-compose has recently changed. Carefully read the [Migration guide](https://jokob-sk.github.io/NetAlertX/MIGRATION/?h=migrat#12-migration-from-netalertx-v25524) for detailed instructions.
|
||||||
> If you are using the currently live production image, please follow the instructions on [Docker Hub](https://hub.docker.com/r/jokobsk/netalertx) for building and running the container.
|
|
||||||
> These docs reflect the latest development version and may differ from the production image.
|
|
||||||
|
|
||||||
This guide assumes you are starting with the official `docker-compose.yml` file provided with the project. We strongly recommend you start with or migrate to this file as your baseline and modify it to suit your specific needs (e.g., changing file paths). While there are many ways to configure NetAlertX, the default file is designed to meet the mandatory security baseline with layer-2 networking capabilities while operating securely and without startup warnings.
|
This guide assumes you are starting with the official `docker-compose.yml` file provided with the project. We strongly recommend you start with or migrate to this file as your baseline and modify it to suit your specific needs (e.g., changing file paths). While there are many ways to configure NetAlertX, the default file is designed to meet the mandatory security baseline with layer-2 networking capabilities while operating securely and without startup warnings.
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ In the **Environment variables** section of Portainer, add the following:
|
|||||||
>
|
>
|
||||||
> `sudo chown -R 20211:20211 /local_data_dir`
|
> `sudo chown -R 20211:20211 /local_data_dir`
|
||||||
>
|
>
|
||||||
> `sudo chmod -R a+rwx /local_data_dir1`
|
> `sudo chmod -R a+rwx /local_data_dir`
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ NetAlertX requires certain paths to be writable at runtime. These paths should b
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm --name netalertx --user "0" \
|
docker run -it --rm --name netalertx --user "0" \
|
||||||
-v /local_data_dir/config:/data/config \
|
-v /local_data_dir:/data \
|
||||||
-v /local_data_dir/db:/data/db \
|
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
ghcr.io/jokob-sk/netalertx:latest
|
ghcr.io/jokob-sk/netalertx:latest
|
||||||
```
|
```
|
||||||
@@ -63,7 +62,7 @@ docker run -it --rm --name netalertx --user "0" \
|
|||||||
>
|
>
|
||||||
> `sudo chown -R 20211:20211 /local_data_dir`
|
> `sudo chown -R 20211:20211 /local_data_dir`
|
||||||
>
|
>
|
||||||
> `sudo chmod -R a+rwx /local_data_dir1`
|
> `sudo chmod -R a+rwx /local_data_dir`
|
||||||
>
|
>
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -84,8 +83,7 @@ services:
|
|||||||
- NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan)
|
- NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan)
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /local_data_dir/config:/data/config
|
- /local_data_dir:/data
|
||||||
- /local_data_dir/db:/data/db
|
|
||||||
- /etc/localtime:/etc/localtime
|
- /etc/localtime:/etc/localtime
|
||||||
environment:
|
environment:
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# NetAlertX Community Helper Scripts Overview
|
# Community Helper Scripts Overview
|
||||||
|
|
||||||
This page provides an overview of community-contributed scripts for NetAlertX. These scripts are not actively maintained and are provided as-is.
|
This page provides an overview of community-contributed scripts for NetAlertX. These scripts are not actively maintained and are provided as-is.
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ To download and install NetAlertX on the hardware/server directly use the `curl`
|
|||||||
> Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**.
|
> Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**.
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may
|
> A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may be dangerous, as you cannot see the code that's about to be executed on your system.
|
||||||
be dangerous, as you cannot see the code that's about to be executed on your system.
|
|
||||||
|
|
||||||
If you trust this repo, you can download the install script via one of the methods (curl/wget) below and it will fo its best to install NetAlertX on your system.
|
If you trust this repo, you can download the install script via one of the methods (curl/wget) below and it will fo its best to install NetAlertX on your system.
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
# Migration
|
# Migration
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> ⚠️ **Important:** The documentation has been recently updated and some instructions may have changed.
|
|
||||||
> If you are using the currently live production image, please follow the instructions on [Docker Hub](https://hub.docker.com/r/jokobsk/netalertx) for building and running the container.
|
|
||||||
> These docs reflect the latest development version and may differ from the production image.
|
|
||||||
|
|
||||||
|
|
||||||
When upgrading from older versions of NetAlertX (or PiAlert by jokob-sk), follow the migration steps below to ensure your data and configuration are properly transferred.
|
When upgrading from older versions of NetAlertX (or PiAlert by jokob-sk), follow the migration steps below to ensure your data and configuration are properly transferred.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
@@ -218,7 +212,7 @@ services:
|
|||||||
|
|
||||||
### 1.3 Migration from NetAlertX `v25.10.1`
|
### 1.3 Migration from NetAlertX `v25.10.1`
|
||||||
|
|
||||||
Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md).
|
Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md). The data location has also hanged from `/app/db` and `/app/config` to `/data/db` and `/data/config`. See detailed steps below.
|
||||||
|
|
||||||
#### STEPS:
|
#### STEPS:
|
||||||
|
|
||||||
@@ -234,8 +228,8 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /local_data_dir/config:/data/config
|
- /local_data_dir/config:/app/config
|
||||||
- /local_data_dir/db:/data/db
|
- /local_data_dir/db:/app/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- /local_data_dir/logs:/tmp/log
|
- /local_data_dir/logs:/tmp/log
|
||||||
environment:
|
environment:
|
||||||
@@ -245,30 +239,7 @@ services:
|
|||||||
|
|
||||||
4. Start the container and verify everything works as expected.
|
4. Start the container and verify everything works as expected.
|
||||||
5. Stop the container.
|
5. Stop the container.
|
||||||
6. Perform a one-off migration to the latest `netalertx` image and `20211` user:
|
6. Update the `docker-compose.yml` as per example below.
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> The example below assumes your `/config` and `/db` folders are stored in `local_data_dir`.
|
|
||||||
> Replace this path with your actual configuration directory. `netalertx` is the container name, which might differ from your setup.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
docker run -it --rm --name netalertx --user "0" \
|
|
||||||
-v /local_data_dir/config:/data/config \
|
|
||||||
-v /local_data_dir/db:/data/db \
|
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
|
||||||
ghcr.io/jokob-sk/netalertx:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
..or alternatively execute:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo chown -R 20211:20211 /local_data_dir/config
|
|
||||||
sudo chown -R 20211:20211 /local_data_dir/db
|
|
||||||
sudo chmod -R a+rwx /local_data_dir/
|
|
||||||
```
|
|
||||||
|
|
||||||
7. Stop the container
|
|
||||||
8. Update the `docker-compose.yml` as per example below.
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
@@ -284,10 +255,7 @@ services:
|
|||||||
- NET_BIND_SERVICE # 🆕 New line
|
- NET_BIND_SERVICE # 🆕 New line
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /local_data_dir/config:/data/config
|
- /local_data_dir:/data # 🆕 This folder contains your /db and /config directories and the parent changed from /app to /data
|
||||||
- /local_data_dir/db:/data/db
|
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
|
||||||
#- /local_data_dir/logs:/tmp/log
|
|
||||||
# Ensuring the timezone is the same as on the server - make sure also the TIMEZONE setting is configured
|
# Ensuring the timezone is the same as on the server - make sure also the TIMEZONE setting is configured
|
||||||
- /etc/localtime:/etc/localtime:ro # 🆕 New line
|
- /etc/localtime:/etc/localtime:ro # 🆕 New line
|
||||||
environment:
|
environment:
|
||||||
@@ -298,5 +266,36 @@ services:
|
|||||||
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||||
# 🆕 New "tmpfs" section END 🔼
|
# 🆕 New "tmpfs" section END 🔼
|
||||||
```
|
```
|
||||||
|
7. Perform a one-off migration to the latest `netalertx` image and `20211` user.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The examples below assumes your `/config` and `/db` folders are stored in `local_data_dir`.
|
||||||
|
> Replace this path with your actual configuration directory. `netalertx` is the container name, which might differ from your setup.
|
||||||
|
|
||||||
|
**Automated approach**:
|
||||||
|
|
||||||
|
Run the container with the `--user "0"` parameter. Please note, some systems will require the manual approach below.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run -it --rm --name netalertx --user "0" \
|
||||||
|
-v /local_data_dir/config:/app/config \
|
||||||
|
-v /local_data_dir/db:/app/db \
|
||||||
|
-v /local_data_dir:/data \
|
||||||
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
|
ghcr.io/jokob-sk/netalertx:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop the container and run it as you would normally.
|
||||||
|
|
||||||
|
**Manual approach**:
|
||||||
|
|
||||||
|
Use the manual approach if the Automated approach fails. Execute the below commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown -R 20211:20211 /local_data_dir
|
||||||
|
sudo chmod -R a+rwx /local_data_dir
|
||||||
|
```
|
||||||
|
|
||||||
|
8. Start the container and verify everything works as expeexpected.
|
||||||
|
9. Check the [Permissions -> Writable-paths](https://jokob-sk.github.io/NetAlertX/FILE_PERMISSIONS/#writable-paths) what directories to mount if you'd like to access the API or log files.
|
||||||
|
|
||||||
9. Start the container and verify everything works as expected.
|
|
||||||
@@ -1,8 +1,29 @@
|
|||||||
# Integration with PiHole
|
# Integration with PiHole
|
||||||
|
|
||||||
NetAlertX comes with 2 plugins suitable for integrating with your existing PiHole instance. One plugin is using a direct SQLite DB connection, the other leverages the DHCP.leases file generated by PiHole. You can combine both approaches and also supplement it with other [plugins](/docs/PLUGINS.md).
|
NetAlertX comes with 3 plugins suitable for integrating with your existing PiHole instance. The first plugin uses the v6 API, the second plugin is using a direct SQLite DB connection, the other leverages the `DHCP.leases` file generated by PiHole. You can combine multiple approaches and also supplement scans with other [plugins](/docs/PLUGINS.md).
|
||||||
|
|
||||||
## Approach 1: `DHCPLSS` Plugin - Import devices from the PiHole DHCP leases file
|
## Approach 1: `PIHOLEAPI` Plugin - Import devices directly from PiHole v6 API
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To use this approach make sure the Web UI password in **Pi-hole** is set.
|
||||||
|
|
||||||
|
| Setting | Description | Recommended value |
|
||||||
|
| :------------- | :------------- | :-------------|
|
||||||
|
| `PIHOLEAPI_URL` | Your Pi-hole base URL including port. | `http://192.168.1.82:9880/` |
|
||||||
|
| `PIHOLEAPI_RUN_SCHD` | If you run multiple device scanner plugins, align the schedules of all plugins to the same value. | `*/5 * * * *` |
|
||||||
|
| `PIHOLEAPI_PASSWORD` | The Web UI base64 encoded (en-/decoding handled by the app) admin password. | `passw0rd` |
|
||||||
|
| `PIHOLEAPI_SSL_VERIFY` | Whether to verify HTTPS certificates. Disable only for self-signed certificates. | `False` |
|
||||||
|
| `PIHOLEAPI_API_MAXCLIENTS` | Maximum number of devices to request from Pi-hole. Defaults are usually fine. | `500` |
|
||||||
|
| `PIHOLEAPI_FAKE_MAC` | Generate FAKE MAC from IP. | `False` |
|
||||||
|
|
||||||
|
Check the [PiHole API plugin readme](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pihole_api_scan/) for details and troubleshooting.
|
||||||
|
|
||||||
|
### docker-compose changes
|
||||||
|
|
||||||
|
No changes needed
|
||||||
|
|
||||||
|
## Approach 2: `DHCPLSS` Plugin - Import devices from the PiHole DHCP leases file
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -23,7 +44,7 @@ Check the [DHCPLSS plugin readme](https://github.com/jokob-sk/NetAlertX/tree/mai
|
|||||||
| `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole`) |
|
| `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole`) |
|
||||||
|
|
||||||
|
|
||||||
## Approach 2: `PIHOLE` Plugin - Import devices directly from the PiHole database
|
## Approach 3: `PIHOLE` Plugin - Import devices directly from the PiHole database
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
|||||||
|
|
||||||
#### ♻ Misc
|
#### ♻ Misc
|
||||||
|
|
||||||
- [Version history (legacy)](./VERSIONS_HISTORY.md)
|
|
||||||
- [Reverse proxy (Nginx, Apache, SWAG)](./REVERSE_PROXY.md)
|
- [Reverse proxy (Nginx, Apache, SWAG)](./REVERSE_PROXY.md)
|
||||||
- [Installing Updates](./UPDATES.md)
|
- [Installing Updates](./UPDATES.md)
|
||||||
- [Setting up Authelia](./AUTHELIA.md) (DRAFT)
|
- [Setting up Authelia](./AUTHELIA.md) (DRAFT)
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ You can configure a custom **/etc/resolv.conf** file in **docker-compose.yml** a
|
|||||||
#### docker-compose.yml:
|
#### docker-compose.yml:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3"
|
|
||||||
services:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Sessions Section in Device View
|
# Sessions Section – Device View
|
||||||
|
|
||||||
The **Sessions Section** provides details about a device's connection history. This data is automatically detected and cannot be edited by the user.
|
The **Sessions Section** shows a device’s connection history. All data is automatically detected and **cannot be edited**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -8,55 +8,57 @@ The **Sessions Section** provides details about a device's connection history. T
|
|||||||
|
|
||||||
## Key Fields
|
## Key Fields
|
||||||
|
|
||||||
1. **Date and Time of First Connection**
|
| Field | Description | Editable? |
|
||||||
- **Description:** Displays the first detected connection time for the device.
|
| ------------------------------ | ------------------------------------------------------------------------------------------------ | --------------- |
|
||||||
- **Editability:** Uneditable (auto-detected).
|
| **First Connection** | The first time the device was detected on the network. | ❌ Auto-detected |
|
||||||
- **Source:** Automatically captured when the device is first added to the system.
|
| **Last Connection** | The most recent time the device was online. | ❌ Auto-detected |
|
||||||
|
|
||||||
2. **Date and Time of Last Connection**
|
|
||||||
- **Description:** Shows the most recent time the device was online.
|
|
||||||
- **Editability:** Uneditable (auto-detected).
|
|
||||||
- **Source:** Updated with every new connection event.
|
|
||||||
|
|
||||||
3. **Offline Devices with Missing or Conflicting Data**
|
|
||||||
- **Description:** Handles cases where a device is offline but has incomplete or conflicting session data (e.g., missing start times).
|
|
||||||
- **Handling:** The system flags these cases for review and attempts to infer missing details.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How Sessions are Discovered and Calculated
|
## How Session Information Works
|
||||||
|
|
||||||
### 1. Detecting New Devices
|
### 1. Detecting New Devices
|
||||||
When a device is first detected in the network, the system logs it in the events table:
|
|
||||||
|
|
||||||
`INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime, eve_EventType, eve_AdditionalInfo, eve_PendingAlertEmail) SELECT cur_MAC, cur_IP, '{startTime}', 'New Device', cur_Vendor, 1 FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Devices WHERE devMac = cur_MAC)`
|
* New devices are automatically detected when they first appear on the network.
|
||||||
|
* A **New Device** record is created, capturing the MAC, IP, vendor, and detection time.
|
||||||
|
|
||||||
- Devices scanned in the current cycle (**CurrentScan**) are checked against the **Devices** table.
|
### 2. Recording Connection Sessions
|
||||||
- If a device is new:
|
|
||||||
- A **New Device** event is logged.
|
|
||||||
- The device’s MAC, IP, vendor, and detection time are recorded.
|
|
||||||
|
|
||||||
### 2. Logging Connection Sessions
|
* Every time a device connects, a session entry is created.
|
||||||
When a new connection is detected, the system creates a session record:
|
* Captured details include:
|
||||||
|
|
||||||
`INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection, ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo) SELECT cur_MAC, cur_IP, 'Connected', '{startTime}', NULL, NULL, 1, cur_Vendor FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Sessions WHERE ses_MAC = cur_MAC)`
|
* Connection type (wired or wireless)
|
||||||
|
* Connection time
|
||||||
- A new session is logged in the **Sessions** table if no prior session exists.
|
* Device details (MAC, IP, vendor)
|
||||||
- Fields like `MAC`, `IP`, `Connection Type`, and `Connection Time` are populated.
|
|
||||||
- The `Still Connected` flag is set to `1` (active connection).
|
|
||||||
|
|
||||||
### 3. Handling Missing or Conflicting Data
|
### 3. Handling Missing or Conflicting Data
|
||||||
- Devices with incomplete or conflicting session data (e.g., missing start times) are detected.
|
|
||||||
- The system flags these records and attempts corrections by inferring details from available data.
|
* **Triggers:**
|
||||||
|
Devices are flagged when session data is incomplete, inconsistent, or conflicting. Examples include:
|
||||||
|
|
||||||
|
* Missing first or last connection timestamps
|
||||||
|
* Overlapping session records
|
||||||
|
* Sessions showing a device as connected and disconnected at the same time
|
||||||
|
|
||||||
|
* **System response:**
|
||||||
|
|
||||||
|
* Automatically highlights affected devices in the **Sessions Section**.
|
||||||
|
* Attempts to **infer missing information** from available data, such as:
|
||||||
|
|
||||||
|
* Estimating first or last connection times from nearby session events
|
||||||
|
* Correcting overlapping session periods
|
||||||
|
* Reconciling conflicting connection statuses
|
||||||
|
|
||||||
|
* **User impact:**
|
||||||
|
|
||||||
|
* Users do **not** need to manually fix session data.
|
||||||
|
* The system ensures the device’s connection history remains as accurate as possible for monitoring and reporting.
|
||||||
|
|
||||||
### 4. Updating Sessions
|
### 4. Updating Sessions
|
||||||
- When a device reconnects, its session is updated with a new connection timestamp.
|
|
||||||
- When a device disconnects:
|
|
||||||
- The **Disconnection Time** is recorded.
|
|
||||||
- The `Still Connected` flag is set to `0`.
|
|
||||||
|
|
||||||
The session information is then used to display the device presence under **Monitoring** -> **Presence**.
|
* **Reconnect:** Updates session with the new connection timestamp.
|
||||||
|
* **Disconnect:** Records disconnection time and marks the device as offline.
|
||||||
|
|
||||||
|
This session information feeds directly into **Monitoring → Presence**, providing a live view of which devices are currently online.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ The folders you are creating below will contain the configuration and the databa
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
5. Open **Container manager** -> **Project** and click **Create**.
|
## Creating the Project
|
||||||
6. Fill in the details:
|
|
||||||
|
1. Open **Container manager** -> **Project** and click **Create**.
|
||||||
|
2. Fill in the details:
|
||||||
|
|
||||||
- Project name: `netalertx`
|
- Project name: `netalertx`
|
||||||
- Path: `/app_storage/netalertx` (will differ from yours)
|
- Path: `/app_storage/netalertx` (will differ from yours)
|
||||||
@@ -31,7 +33,6 @@ The folders you are creating below will contain the configuration and the databa
|
|||||||
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3"
|
|
||||||
services:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
@@ -47,8 +48,7 @@ services:
|
|||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
- NET_BIND_SERVICE
|
- NET_BIND_SERVICE
|
||||||
volumes:
|
volumes:
|
||||||
- /app_storage/netalertx/config:/data/config
|
- /app_storage/netalertx:/data
|
||||||
- /app_storage/netalertx/db:/data/db
|
|
||||||
# to sync with system time
|
# to sync with system time
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
tmpfs:
|
tmpfs:
|
||||||
@@ -60,33 +60,63 @@ services:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
7. Replace the paths to your volume and comment out unnecessary line(s):
|
3. Replace the paths to your volume and comment out unnecessary line(s).
|
||||||
|
|
||||||
- This is only an example, your paths will differ.
|
> This is only an example, your paths will differ.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
volumes:
|
volumes:
|
||||||
- /volume1/app_storage/netalertx/config:/data/config
|
- /volume1/app_storage/netalertx:/data
|
||||||
- /volume1/app_storage/netalertx/db:/data/db
|
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
|
||||||
# - local/path/logs:/tmp/log <- commented out with # ⚠
|
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
8. (optional) Change the port number from `20211` to an unused port if this port is already used.
|
4. (optional) Change the port number from `20211` to an unused port if this port is already used.
|
||||||
9. Build the project:
|
5. Build the project:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
10. Navigate to `<Synology URL>:20211` (or your custom port).
|
10. Navigate to `<Synology URL>:20211` (or your custom port).
|
||||||
11. Read the [Subnets](./SUBNETS.md) and [Plugins](/docs/PLUGINS.md) docs to complete your setup.
|
11. Read the [Subnets](./SUBNETS.md) and [Plugins](/docs/PLUGINS.md) docs to complete your setup.
|
||||||
|
|
||||||
|
## Solving permission issues
|
||||||
|
|
||||||
|
See also the [Permission overview guide](./FILE_PERMISSIONS.md).
|
||||||
|
|
||||||
|
### Configuring the permissions via SSH
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> If you are facing permissions issues run the following commands on your server. This will change the owner and assure sufficient access to the database and config files that are stored in the `/local_data_dir/db` and `/local_data_dir/config` folders (replace `local_data_dir` with the location where your `/db` and `/config` folders are located).
|
> If you are facing permissions issues run the following commands on your server. This will change the owner and assure sufficient access to the database and config files that are stored in the `/local_data_dir/db` and `/local_data_dir/config` folders (replace `local_data_dir` with the location where your `/db` and `/config` folders are located).
|
||||||
>
|
>
|
||||||
> `sudo chown -R 20211:20211 /local_data_dir`
|
> `sudo chown -R 20211:20211 /local_data_dir`
|
||||||
>
|
>
|
||||||
> `sudo chmod -R a+rwx /local_data_dir1`
|
> `sudo chmod -R a+rwx /local_data_dir`
|
||||||
>
|
>
|
||||||
|
|
||||||
|
### Configuring the permissions via the Synology UI
|
||||||
|
|
||||||
|
You can also execute the above bash commands via the UI by creating a one-off scheduled task.
|
||||||
|
|
||||||
|
1. Control panel -> Task Scheduler
|
||||||
|
2. Create -> Scheduled Task -> User-defined Script
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3. Give your task a name.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. Specify one-off execution time (e.g. 5 minutes from now).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5. Paste the commands from the above SSH section and replace the `/local_data_dir` with the parent fodler of your `/db` and `/config` folders.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
6. Wait until the execution time passes and verify the new ownership.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
In case of issues, double-check the [Permission overview guide](./FILE_PERMISSIONS.md).
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> For versions prior to `v25.6.7` upgrade to version `v25.5.24` first (`docker pull ghcr.io/jokob-sk/netalertx:25.5.24`) as later versions don't support a full upgrade. Alternatively, devices and settings can be migrated manually, e.g. via [CSV import](./DEVICES_BULK_EDITING.md).
|
> For versions prior to `v25.6.7` upgrade to version `v25.5.24` first (`docker pull ghcr.io/jokob-sk/netalertx:25.5.24`) as later versions don't support a full upgrade. Alternatively, devices and settings can be migrated manually, e.g. via [CSV import](./DEVICES_BULK_EDITING.md).
|
||||||
|
> See the [Migration guide](./MIGRATION.md) for details.
|
||||||
|
|
||||||
This guide outlines approaches for updating Docker containers, usually when upgrading to a newer version of NetAlertX. Each method offers different benefits depending on the situation. Here are the methods:
|
This guide outlines approaches for updating Docker containers, usually when upgrading to a newer version of NetAlertX. Each method offers different benefits depending on the situation. Here are the methods:
|
||||||
|
|
||||||
|
|||||||
BIN
docs/img/PIHOLE_GUIDE/PIHOLEAPI_settings.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 23 KiB |
BIN
docs/img/SYNOLOGY/10_permissions_before.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/img/SYNOLOGY/11_permissions_create_scheduled_task.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/SYNOLOGY/12_permissions_task_general.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/SYNOLOGY/13_permissions_task_schedule.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/SYNOLOGY/14_permissions_task_settings.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/SYNOLOGY/15_permissions_after.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.9 KiB |
BIN
docs/img/netalertx_docs_old2.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
@@ -70,6 +70,11 @@ a[target="_blank"] {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-is-valid="0"] {
|
||||||
|
/* border: 1px solid red; */
|
||||||
|
background-color: #ff4b4b !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
Helper Classes
|
Helper Classes
|
||||||
----------------------------------------------------------------------------- */
|
----------------------------------------------------------------------------- */
|
||||||
@@ -1820,10 +1825,21 @@ input[readonly] {
|
|||||||
#networkTree
|
#networkTree
|
||||||
{
|
{
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
/* border: solid;
|
|
||||||
border-color:#606060; */
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#networkTree .node-inner {
|
||||||
|
font-size: clamp(12px, 1rem, 18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#networkTree .netNodeText strong,
|
||||||
|
#networkTree .spanNetworkTree {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
#networkTree .netIcon
|
#networkTree .netIcon
|
||||||
{
|
{
|
||||||
width: 25px;
|
width: 25px;
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
<!-- page script ----------------------------------------------------------- -->
|
<!-- page script ----------------------------------------------------------- -->
|
||||||
<script>
|
<script>
|
||||||
var deviceStatus = 'all';
|
var deviceStatus = 'all';
|
||||||
var tableRows = getCache ("nax_parTableRows") == "" ? parseInt(getSetting("UI_DEFAULT_PAGE_SIZE")) : getCache ("nax_parTableRows") ;
|
|
||||||
var tableOrder = getCache ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCache ("nax_parTableOrder")) ;
|
var tableOrder = getCache ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCache ("nax_parTableOrder")) ;
|
||||||
|
|
||||||
var tableColumnHide = [];
|
var tableColumnHide = [];
|
||||||
@@ -563,6 +563,9 @@ function initializeDatatable (status) {
|
|||||||
status = 'my_devices'
|
status = 'my_devices'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// retrieve page size
|
||||||
|
var tableRows = getCache ("nax_parTableRows") == "" ? parseInt(getSetting("UI_DEFAULT_PAGE_SIZE")) : getCache ("nax_parTableRows") ;
|
||||||
|
|
||||||
// Save status selected
|
// Save status selected
|
||||||
deviceStatus = status;
|
deviceStatus = status;
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.5 KiB |
452
front/img/svg/netalertx_docs_blue.svg
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
viewBox="0 0 52.916667 52.916668"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||||
|
sodipodi:docname="netalertx_red_docs_copy3_blue.svg"
|
||||||
|
inkscape:export-filename="C:\Users\jokob\netalertx_red_docs_d_1.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="132.40575"
|
||||||
|
inkscape:cy="118.44039"
|
||||||
|
inkscape:window-width="3377"
|
||||||
|
inkscape:window-height="1417"
|
||||||
|
inkscape:window-x="55"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g48055"
|
||||||
|
units="px"
|
||||||
|
width="50px" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="powermask"
|
||||||
|
id="path-effect51283"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
uri="#mask-powermask-path-effect51283"
|
||||||
|
invert="false"
|
||||||
|
hide_mask="false"
|
||||||
|
background="true"
|
||||||
|
background_color="#ffffffff" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="powermask"
|
||||||
|
id="path-effect51278"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
uri="#mask-powermask-path-effect51278"
|
||||||
|
invert="false"
|
||||||
|
hide_mask="false"
|
||||||
|
background="true"
|
||||||
|
background_color="#ffffffff" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="powermask"
|
||||||
|
id="path-effect51273"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
uri="#mask-powermask-path-effect51273"
|
||||||
|
invert="false"
|
||||||
|
hide_mask="false"
|
||||||
|
background="true"
|
||||||
|
background_color="#ffffffff" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="powermask"
|
||||||
|
id="path-effect48754"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
uri="#mask-powermask-path-effect48754"
|
||||||
|
invert="false"
|
||||||
|
hide_mask="false"
|
||||||
|
background="true"
|
||||||
|
background_color="#ffffffff" />
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath48972">
|
||||||
|
<path
|
||||||
|
style="fill:#000000;stroke-width:0.280643"
|
||||||
|
id="path48974"
|
||||||
|
width="56.128242"
|
||||||
|
height="56.128246"
|
||||||
|
x="-18.924671"
|
||||||
|
y="-56.198174"
|
||||||
|
transform="rotate(45.438374)"
|
||||||
|
mask="none"
|
||||||
|
sodipodi:type="rect" />
|
||||||
|
</clipPath>
|
||||||
|
<mask
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
id="mask49405">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:60.8695px;line-height:1.25;font-family:Amiri;-inkscape-font-specification:Amiri;display:inline;stroke-width:1.52174"
|
||||||
|
x="66.930733"
|
||||||
|
y="78.642288"
|
||||||
|
id="text49409"
|
||||||
|
transform="scale(1.4861626,0.67287388)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan49407"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Tw Cen MT';-inkscape-font-specification:'Tw Cen MT';fill:#ffffff;stroke-width:1.52174"
|
||||||
|
x="66.930733"
|
||||||
|
y="78.642288">A</tspan></text>
|
||||||
|
</mask>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath50306">
|
||||||
|
<circle
|
||||||
|
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||||
|
id="circle50308"
|
||||||
|
cy="26.458334"
|
||||||
|
cx="26.458334"
|
||||||
|
r="26.458334"
|
||||||
|
clip-path="url(#clipPath48972)"
|
||||||
|
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath48972-7">
|
||||||
|
<path
|
||||||
|
style="fill:#000000;stroke-width:0.280643"
|
||||||
|
id="path48974-5"
|
||||||
|
width="56.128242"
|
||||||
|
height="56.128246"
|
||||||
|
x="-18.924671"
|
||||||
|
y="-56.198174"
|
||||||
|
transform="rotate(45.438374)"
|
||||||
|
mask="none"
|
||||||
|
sodipodi:type="rect" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath50306-6">
|
||||||
|
<circle
|
||||||
|
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||||
|
id="circle50308-5"
|
||||||
|
cy="26.458334"
|
||||||
|
cx="26.458334"
|
||||||
|
r="26.458334"
|
||||||
|
clip-path="url(#clipPath48972)"
|
||||||
|
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||||
|
</clipPath>
|
||||||
|
<mask
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
id="mask-powermask-path-effect51273">
|
||||||
|
<path
|
||||||
|
id="mask-powermask-path-effect51273_box"
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="m 71.788348,33.677177 h 2.00083 v 2.173766 h -2.00083 z" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
id="path51263"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="66.211845"
|
||||||
|
sodipodi:cy="37.490814"
|
||||||
|
sodipodi:rx="3.9464016"
|
||||||
|
sodipodi:ry="1.4616301"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="0.031086059"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
d="m 70.158247,37.490814 a 3.9464016,1.4616301 0 0 1 -0.0019,0.04543" />
|
||||||
|
</mask>
|
||||||
|
<mask
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
id="mask-powermask-path-effect51278">
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
id="path51267"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="66.211845"
|
||||||
|
sodipodi:cy="37.490814"
|
||||||
|
sodipodi:rx="3.9464016"
|
||||||
|
sodipodi:ry="1.4616301"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="0.031086059"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:arc-type="arc" />
|
||||||
|
</mask>
|
||||||
|
<mask
|
||||||
|
maskUnits="userSpaceOnUse"
|
||||||
|
id="mask-powermask-path-effect51283">
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
id="path51271"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="66.211845"
|
||||||
|
sodipodi:cy="37.490814"
|
||||||
|
sodipodi:rx="3.9464016"
|
||||||
|
sodipodi:ry="1.4616301"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="0.031086059"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:arc-type="arc" />
|
||||||
|
</mask>
|
||||||
|
<filter
|
||||||
|
id="mask-powermask-path-effect51273_inverse"
|
||||||
|
inkscape:label="filtermask-powermask-path-effect51273"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
height="100"
|
||||||
|
width="100"
|
||||||
|
x="-50"
|
||||||
|
y="-50">
|
||||||
|
<feColorMatrix
|
||||||
|
id="mask-powermask-path-effect51273_primitive1"
|
||||||
|
values="1"
|
||||||
|
type="saturate"
|
||||||
|
result="fbSourceGraphic" />
|
||||||
|
<feColorMatrix
|
||||||
|
id="mask-powermask-path-effect51273_primitive2"
|
||||||
|
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||||
|
in="fbSourceGraphic" />
|
||||||
|
</filter>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath1481">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;stroke-width:0.227484"
|
||||||
|
id="rect1483"
|
||||||
|
width="26.653997"
|
||||||
|
height="52.852543"
|
||||||
|
x="62.86179"
|
||||||
|
y="-0.46772188" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath
|
||||||
|
clipPathUnits="userSpaceOnUse"
|
||||||
|
id="clipPath1481-1">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;stroke-width:0.227484"
|
||||||
|
id="rect1483-0"
|
||||||
|
width="26.653997"
|
||||||
|
height="52.852543"
|
||||||
|
x="62.86179"
|
||||||
|
y="-0.46772188" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Red 1"
|
||||||
|
style="display:none">
|
||||||
|
<circle
|
||||||
|
style="fill:#ff2a2a;stroke-width:0.176318"
|
||||||
|
id="path31-8"
|
||||||
|
cy="26.458334"
|
||||||
|
cx="26.458334"
|
||||||
|
r="26.458334" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="A - Layer 2"
|
||||||
|
style="display:none">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;stroke-width:0.328992"
|
||||||
|
id="rect48998"
|
||||||
|
width="26.0966"
|
||||||
|
height="6.0620313"
|
||||||
|
x="13.255443"
|
||||||
|
y="41.262722" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="g48055"
|
||||||
|
inkscape:label="Red top"
|
||||||
|
style="display:none;mix-blend-mode:normal">
|
||||||
|
<circle
|
||||||
|
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||||
|
id="circle48752"
|
||||||
|
cy="26.458334"
|
||||||
|
cx="26.458334"
|
||||||
|
r="26.458334"
|
||||||
|
clip-path="url(#clipPath48972)"
|
||||||
|
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||||
|
<ellipse
|
||||||
|
style="display:inline;mix-blend-mode:normal;fill:#000000;stroke-width:0.43638"
|
||||||
|
id="path50080"
|
||||||
|
clip-path="url(#clipPath50306)"
|
||||||
|
ry="13.739323"
|
||||||
|
rx="16.735666"
|
||||||
|
cy="22.874514"
|
||||||
|
cx="26.36149"
|
||||||
|
transform="translate(0,0.09980904)" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
id="path51325"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="16.772207"
|
||||||
|
sodipodi:cy="26.090099"
|
||||||
|
sodipodi:rx="4.1291056"
|
||||||
|
sodipodi:ry="7.6004772"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="0.031086059"
|
||||||
|
sodipodi:arc-type="slice"
|
||||||
|
d="m 20.901313,26.090099 a 4.1291056,7.6004772 0 0 1 -0.002,0.236231 l -4.127111,-0.236231 z" />
|
||||||
|
<path
|
||||||
|
style="fill:#d40000"
|
||||||
|
id="path51717"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="26.441042"
|
||||||
|
sodipodi:cy="-26.531424"
|
||||||
|
sodipodi:rx="10.418671"
|
||||||
|
sodipodi:ry="9.5820541"
|
||||||
|
sodipodi:start="0.82219863"
|
||||||
|
sodipodi:end="2.3054129"
|
||||||
|
sodipodi:arc-type="slice"
|
||||||
|
d="m 33.532115,-19.511189 a 10.418671,9.5820541 0 0 1 -14.074736,0.09049 l 6.983663,-7.110726 z"
|
||||||
|
transform="matrix(1,0,0.0048047,-0.99998846,0,0)" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;stroke-width:0.276214"
|
||||||
|
d="M 145.28835,50.354872 C 127.01317,34.62734 98.057144,30.012421 73.710372,38.947003 c -6.518003,2.391924 -14.288822,6.834002 -19.265958,11.01311 -1.198654,1.006465 -2.270358,1.829935 -2.381565,1.829935 -0.111206,0 -5.210052,-5.102002 -11.33077,-11.337781 L 29.603503,29.114489 30.822139,27.851613 c 0.670251,-0.69458 2.51592,-2.384634 4.101489,-3.755674 C 50.725112,10.43241 69.462577,2.3767456 90.736164,0.10085492 95.380582,-0.39601422 106.33043,-0.31105699 111.03786,0.25837091 133.04363,2.9202648 151.46536,11.26468 167.83762,25.986722 l 3.30701,2.97369 -2.29392,2.320103 c -1.26165,1.276057 -6.58213,6.517685 -11.82329,11.648065 l -9.52936,9.327957 z"
|
||||||
|
id="path52311"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;stroke-width:0.276214"
|
||||||
|
d="M 86.538548,86.634546 74.145111,73.25799 74.899337,72.758689 c 4.93766,-3.268754 10.138703,-6.508578 16.602198,-7.437693 5.484021,-0.788317 12.228205,-0.984814 16.377135,-0.09119 6.77689,1.459652 11.87156,4.340971 17.02452,7.792011 l 0.97468,0.652765 -1.37124,1.269268 c -0.86863,0.804036 -6.82647,6.676301 -13.34742,13.259175 L 99.423152,99.796276 Z"
|
||||||
|
id="path52350"
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
inkscape:export-filename="C:\Users\jokob\path52350.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
sodipodi:nodetypes="ccsssscsscc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="Black"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline">
|
||||||
|
<ellipse
|
||||||
|
style="fill:#000000;stroke-width:0.176146"
|
||||||
|
id="path31"
|
||||||
|
cy="26.51001"
|
||||||
|
cx="26.458334"
|
||||||
|
rx="26.458"
|
||||||
|
ry="26.406658" />
|
||||||
|
<circle
|
||||||
|
style="display:inline;fill:#ffffff;stroke-width:0.176318"
|
||||||
|
id="path31-89"
|
||||||
|
mask="url(#mask49405)"
|
||||||
|
transform="translate(-99.990036,0.02979629)"
|
||||||
|
r="26.458334"
|
||||||
|
cy="26.458334"
|
||||||
|
cx="126.45834" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="M 50.734917,51.5385 C 50.317784,51.008202 45.376222,45.855755 39.753667,40.088624 L 29.530842,29.602927 32.157037,27.108298 C 37.014258,22.494413 44.043654,17.26825 51.002109,13.097503 60.785219,7.2337198 74.185013,2.5922331 86.866814,0.67450934 92.65309,-0.20048258 104.71024,-0.37258331 110.80487,0.33282367 133.37755,2.9454414 150.98136,11.201829 167.87245,27.098183 l 2.76303,2.600302 -11.44673,11.421726 -11.44672,11.421723 -2.63001,-2.20425 C 135.80913,42.540775 123.7472,37.357565 110.13188,35.306142 105.25895,34.571936 94.151456,34.473316 89.625785,35.124073 76.006414,37.082441 65.655848,41.542025 54.928431,50.073566 c -1.679878,1.336011 -3.139997,2.429113 -3.244707,2.429113 -0.104711,0 -0.531674,-0.433881 -0.948807,-0.964179 z"
|
||||||
|
id="path117144"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="m 86.479201,86.655988 -12.859682,-12.863304 1.72756,-1.259375 c 5.937867,-4.328648 15.716974,-7.877579 22.763988,-8.261269 5.344243,-0.290978 12.593953,1.304433 19.011433,4.183761 2.41258,1.08245 8.21218,4.752269 8.21218,5.196429 0,0.224653 -16.50779,16.711429 -23.16256,23.133076 l -2.833236,2.733985 z"
|
||||||
|
id="path117183"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="m 151.14408,181.37289 -2.63396,-2.65165 H 99.719219 50.928317 l -2.558625,2.54155 c -2.367982,2.35218 -2.618861,2.50924 -3.367071,2.10794 -1.632484,-0.87558 -7.984339,-5.82527 -11.691442,-9.11058 l -3.811927,-3.3782 34.882231,-35.14801 c 19.185224,-19.3314 34.980859,-35.144 35.101403,-35.13912 0.120544,0.005 16.129074,15.83285 35.574514,35.17326 l 35.35534,35.16438 -2.12132,1.95782 c -4.15184,3.83183 -13.51513,11.13426 -14.27653,11.13426 -0.13027,0 -1.42214,-1.19324 -2.87081,-2.65165 z M 112.69455,143.27811 99.52528,130.10884 86.35601,143.27811 73.18674,156.44738 h 26.33854 26.33854 z"
|
||||||
|
id="path117222"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="m 43.323744,182.02493 c -3.122315,-2.21745 -8.886633,-6.91043 -11.466851,-9.33566 l -2.129855,-2.00191 31.417516,-31.60357 c 17.279634,-17.38196 32.982312,-33.19165 34.894842,-35.13266 l 3.477325,-3.5291 35.271229,35.28278 35.27123,35.28278 -2.29809,2.12417 c -3.23874,2.99361 -8.21439,6.9674 -11.21429,8.95625 l -2.55224,1.69205 -2.04396,-1.77268 c -1.12418,-0.97498 -2.34704,-2.10872 -2.71748,-2.51941 l -0.67351,-0.74673 H 99.52196 50.484308 l -2.199537,2.47487 c -1.209746,1.36118 -2.306828,2.46959 -2.437961,2.46312 -0.131132,-0.006 -1.266512,-0.7419 -2.523066,-1.6343 z m 82.364486,-25.84451 c 0,-0.14683 -5.88666,-6.15201 -13.08147,-13.34485 L 99.52528,129.75768 86.443805,142.83557 c -7.194812,7.19284 -13.081476,13.19802 -13.081476,13.34485 0,0.14683 11.773328,0.26696 26.162951,0.26696 14.38962,0 26.16295,-0.12013 26.16295,-0.26696 z"
|
||||||
|
id="path117261"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer6"
|
||||||
|
inkscape:label="Circle"
|
||||||
|
style="display:none">
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
id="path50026"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="71.071762"
|
||||||
|
sodipodi:cy="34.677177"
|
||||||
|
sodipodi:rx="1.7174155"
|
||||||
|
sodipodi:ry="5.5907354"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="0.031086059"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
mask="url(#mask-powermask-path-effect51273)"
|
||||||
|
d="m 72.789178,34.677177 a 1.7174155,5.5907354 0 0 1 -8.3e-4,0.173766"
|
||||||
|
inkscape:path-effect="#path-effect51273" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;stroke-width:0.276214"
|
||||||
|
d="m 151.08883,181.46994 -2.76213,-2.60427 -48.802077,-0.009 -48.802075,-0.009 -2.292573,2.48592 c -1.260915,1.36726 -2.431589,2.48592 -2.601499,2.48592 -0.869396,0 -9.118995,-6.36599 -13.713669,-10.58246 l -2.688104,-2.46684 34.973647,-35.11455 c 19.235503,-19.313 34.922993,-35.39075 35.029879,-35.39075 0.106889,0 16.231201,16.10588 35.663001,35.45326 l 35.33055,35.17705 -2.48592,2.35505 c -3.08951,2.92687 -7.41515,6.40509 -11.09719,8.92319 -1.54594,1.05725 -2.85105,1.91728 -2.90024,1.9112 -0.0492,-0.006 -1.33242,-1.183 -2.8516,-2.61535 z m -38.4631,-38.32188 -13.050732,-13.05073 -13.050727,13.05073 -13.050725,13.05072 h 26.101452 26.101452 z"
|
||||||
|
id="path52389"
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
inkscape:export-filename="C:\Users\jokob\path52389.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
sodipodi:nodetypes="ccccssscssscsscccccccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#d40000;stroke-width:0.276214"
|
||||||
|
d="M 86.416478,86.793237 C 73.427951,73.815968 73.387119,73.801376 73.387119,73.801376 c 3.874197,-3.341721 11.025508,-6.981646 17.312424,-8.529335 2.339787,-0.576001 4.881362,-1.25628 8.810591,-1.259564 4.438736,-0.0037 8.292516,0.857843 13.253396,2.535104 4.59135,1.552325 7.8315,3.224336 11.49958,5.934101 l 1.61476,1.192897 -2.31005,2.336325 c -1.27053,1.284978 -7.22284,7.16236 -13.22736,13.060849 L 99.423152,99.796276 C 95.128284,95.409033 87.282899,87.658907 86.416478,86.793237 Z"
|
||||||
|
id="path52465"
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
sodipodi:nodetypes="sssssscsscs" />
|
||||||
|
<path
|
||||||
|
style="fill:#d40000;stroke-width:0.074168"
|
||||||
|
d="M 38.412677,13.39572 C 34.322163,9.945267 28.437517,8.4874766 22.684204,9.4993379 19.419721,10.073478 16.752307,11.410793 13.835187,13.872492 l -0.14691,0.126732 -0.587936,-0.661605 c -0.268568,-0.30222 -1.619514,-1.65761 -2.963235,-3.048642 L 7.7265561,7.8632145 7.9975963,7.5868118 C 9.8344314,5.713635 13.005888,3.476019 15.380049,2.3878744 20.659765,-0.03196726 26.24205,-0.73479764 31.856076,0.42838695 36.599757,1.4112419 40.746004,3.5106537 44.46876,7.1557672 l 0.709881,0.6950753 -0.663694,0.69037 C 44.080041,8.9935983 42.672626,10.391271 41.3963,11.655819 L 39.075708,13.955 Z"
|
||||||
|
id="path52504"
|
||||||
|
inkscape:export-filename="C:\Users\jokob\path52504.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
sodipodi:nodetypes="ssscsccsssscsscs" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="M 86.655143,86.478376 73.973101,73.792663 75.700647,72.517799 c 3.888483,-2.869556 11.979097,-6.234087 17.887709,-7.438714 6.781224,-1.382532 16.632394,0.1812 23.791374,3.776537 2.53147,1.271345 7.60139,4.47823 7.60139,4.808126 0,0.217537 -18.217,18.402022 -23.34018,23.298518 l -2.303755,2.201823 z"
|
||||||
|
id="path117417"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="M 86.653362,86.476595 74.004328,73.8239 l 1.78137,-1.307646 c 4.058289,-2.979059 11.996346,-6.266814 18.081148,-7.488783 5.742499,-1.153228 13.433334,-0.173122 20.711924,2.639491 2.64803,1.02326 7.63077,3.765523 9.69377,5.334995 l 0.88241,0.67131 -6.36248,6.41376 c -3.49937,3.527567 -9.3162,9.255172 -12.92628,12.728011 l -6.563793,6.314253 z"
|
||||||
|
id="path117456"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="M 40.755089,40.913849 29.891381,29.698485 32.789887,26.931909 C 38.664423,21.324762 48.374309,14.517657 56.038213,10.633695 66.085649,5.5417911 79.271822,1.6347929 90.224457,0.50447904 c 5.29419,-0.54636158 20.003853,-0.24145692 24.614013,0.51020386 16.55879,2.6998184 30.27274,8.3744041 42.56518,17.6127021 3.66685,2.755798 10.38919,8.484428 12.02678,10.248962 l 0.78546,0.846346 -11.22765,11.223531 -11.22764,11.223531 -2.46252,-1.977749 C 130.84681,38.585569 112.25268,33.14502 92.666988,34.792406 78.082451,36.019136 67.49078,40.200159 55.292129,49.545997 c -1.868753,1.431721 -3.459743,2.598649 -3.535534,2.593173 -0.07579,-0.0055 -5.026468,-5.056871 -11.001506,-11.225321 z"
|
||||||
|
id="path117495"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer4"
|
||||||
|
inkscape:label="half circle"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="M 50.729651,51.530407 C 50.309622,50.995658 45.365237,45.839438 39.74213,40.072138 L 29.518298,29.586142 32.436819,26.865215 C 37.858508,21.810591 46.002106,15.887672 52.91436,11.971698 62.082793,6.7775379 75.058024,2.4602175 86.866814,0.67450934 92.666822,-0.20255914 104.7089,-0.37259245 110.83899,0.33602379 133.4335,2.9478667 150.81881,11.108766 167.8709,27.107589 l 2.76147,2.590896 -11.424,11.400559 -11.42399,11.400559 -2.65118,-2.175966 C 132.57167,40.013706 117.00056,34.697228 99.348504,34.691269 c -17.588857,-0.0059 -30.84176,4.583432 -44.420073,15.382297 -1.679878,1.336011 -3.139997,2.429113 -3.244707,2.429113 -0.104711,0 -0.534044,-0.437522 -0.954073,-0.972272 z"
|
||||||
|
id="path117300"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="m 86.479787,86.656574 -12.860268,-12.86389 1.72756,-1.257012 c 5.92724,-4.312793 15.575223,-7.833372 22.587211,-8.242144 5.50807,-0.321098 12.64715,1.227498 19.18821,4.162273 2.41292,1.082605 8.21218,4.752294 8.21218,5.196553 0,0.223831 -14.54007,14.745171 -22.63164,22.602487 l -3.362985,3.26562 z"
|
||||||
|
id="path117339"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.98;fill:#5f5fd3;fill-opacity:1;stroke-width:3.81317;stroke-linecap:round;stroke-miterlimit:0.4"
|
||||||
|
d="m 43.323744,182.02493 c -3.01377,-2.14036 -8.648648,-6.71423 -11.329522,-9.19625 l -2.145795,-1.98662 2.929747,-3.04309 c 1.611361,-1.6737 17.298698,-17.50163 34.860748,-35.17319 l 31.931002,-32.13009 35.244626,35.24359 35.24463,35.2436 -2.29809,2.12652 c -3.22978,2.98865 -8.20792,6.96547 -11.21429,8.95861 l -2.55224,1.69205 -2.04396,-1.77268 c -1.12418,-0.97498 -2.34704,-2.10872 -2.71748,-2.51941 l -0.67351,-0.74673 H 99.52196 50.484308 l -2.199537,2.47487 c -1.209746,1.36118 -2.306828,2.46959 -2.437961,2.46312 -0.131132,-0.006 -1.266512,-0.7419 -2.523066,-1.6343 z m 82.364486,-25.84451 c 0,-0.14683 -5.88666,-6.15201 -13.08147,-13.34485 L 99.52528,129.75768 86.443805,142.83557 c -7.194812,7.19284 -13.081476,13.19802 -13.081476,13.34485 0,0.14683 11.773328,0.26696 26.162951,0.26696 14.38962,0 26.16295,-0.12013 26.16295,-0.26696 z"
|
||||||
|
id="path117378"
|
||||||
|
transform="scale(0.26458333)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 22 KiB |
@@ -378,7 +378,7 @@ function localizeTimestamp(input) {
|
|||||||
let tz = getSetting("TIMEZONE") || 'Europe/Berlin';
|
let tz = getSetting("TIMEZONE") || 'Europe/Berlin';
|
||||||
input = String(input || '').trim();
|
input = String(input || '').trim();
|
||||||
|
|
||||||
// ✅ 1. Unix timestamps (10 or 13 digits)
|
// 1. Unix timestamps (10 or 13 digits)
|
||||||
if (/^\d+$/.test(input)) {
|
if (/^\d+$/.test(input)) {
|
||||||
const ms = input.length === 10 ? parseInt(input, 10) * 1000 : parseInt(input, 10);
|
const ms = input.length === 10 ? parseInt(input, 10) * 1000 : parseInt(input, 10);
|
||||||
return new Intl.DateTimeFormat('default', {
|
return new Intl.DateTimeFormat('default', {
|
||||||
@@ -389,15 +389,21 @@ function localizeTimestamp(input) {
|
|||||||
}).format(new Date(ms));
|
}).format(new Date(ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 2. European DD/MM/YYYY
|
// 2. European DD/MM/YYYY
|
||||||
let match = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[ ,]+(\d{1,2}:\d{2}(?::\d{2})?))?(.*)$/);
|
let match = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[ ,]+(\d{1,2}:\d{2}(?::\d{2})?))?$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
let [, d, m, y, t = "00:00:00", tzPart = ""] = match;
|
let [, d, m, y, t = "00:00:00", tzPart = ""] = match;
|
||||||
|
const dNum = parseInt(d, 10);
|
||||||
|
const mNum = parseInt(m, 10);
|
||||||
|
|
||||||
|
if (dNum <= 12 && mNum > 12) {
|
||||||
|
} else {
|
||||||
const iso = `${y}-${m.padStart(2,'0')}-${d.padStart(2,'0')}T${t.length===5 ? t + ":00" : t}${tzPart}`;
|
const iso = `${y}-${m.padStart(2,'0')}-${d.padStart(2,'0')}T${t.length===5 ? t + ":00" : t}${tzPart}`;
|
||||||
return formatSafe(iso, tz);
|
return formatSafe(iso, tz);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ✅ 3. US MM/DD/YYYY
|
// 3. US MM/DD/YYYY
|
||||||
match = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[ ,]+(\d{1,2}:\d{2}(?::\d{2})?))?(.*)$/);
|
match = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[ ,]+(\d{1,2}:\d{2}(?::\d{2})?))?(.*)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
let [, m, d, y, t = "00:00:00", tzPart = ""] = match;
|
let [, m, d, y, t = "00:00:00", tzPart = ""] = match;
|
||||||
@@ -405,23 +411,37 @@ function localizeTimestamp(input) {
|
|||||||
return formatSafe(iso, tz);
|
return formatSafe(iso, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 4. ISO-style (with T, Z, offsets)
|
// 4. ISO YYYY-MM-DD with optional Z/+offset
|
||||||
match = input.match(/^(\d{4}-\d{1,2}-\d{1,2})[ T](\d{1,2}:\d{2}(?::\d{2})?)(Z|[+-]\d{2}:?\d{2})?$/);
|
match = input.match(/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])[ T](\d{1,2}:\d{2}(?::\d{2})?)(Z|[+-]\d{2}:?\d{2})?$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
let [ , ymd, time, offset = "" ] = match;
|
let [, y, m, d, time, offset = ""] = match;
|
||||||
// normalize to YYYY-MM-DD
|
|
||||||
let [y, m, d] = ymd.split('-').map(x => x.padStart(2,'0'));
|
|
||||||
const iso = `${y}-${m}-${d}T${time.length===5?time+":00":time}${offset}`;
|
const iso = `${y}-${m}-${d}T${time.length===5?time+":00":time}${offset}`;
|
||||||
return formatSafe(iso, tz);
|
return formatSafe(iso, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 5. RFC2822 / "25 Aug 2025 13:45:22 +0200"
|
// 5. RFC2822 / "25 Aug 2025 13:45:22 +0200"
|
||||||
match = input.match(/^\d{1,2} [A-Za-z]{3,} \d{4}/);
|
match = input.match(/^\d{1,2} [A-Za-z]{3,} \d{4}/);
|
||||||
if (match) {
|
if (match) {
|
||||||
return formatSafe(input, tz);
|
return formatSafe(input, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 6. Fallback (whatever Date() can parse)
|
// 6. DD-MM-YYYY with optional time
|
||||||
|
match = input.match(/^(\d{1,2})-(\d{1,2})-(\d{4})(?:[ T](\d{1,2}:\d{2}(?::\d{2})?))?$/);
|
||||||
|
if (match) {
|
||||||
|
let [, d, m, y, time = "00:00:00"] = match;
|
||||||
|
const iso = `${y}-${m.padStart(2,'0')}-${d.padStart(2,'0')}T${time.length===5?time+":00":time}`;
|
||||||
|
return formatSafe(iso, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Strict YYYY-DD-MM with optional time
|
||||||
|
match = input.match(/^(\d{4})-(0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])(?:[ T](\d{1,2}:\d{2}(?::\d{2})?))?$/);
|
||||||
|
if (match) {
|
||||||
|
let [, y, d, m, time = "00:00:00"] = match;
|
||||||
|
const iso = `${y}-${m}-${d}T${time.length === 5 ? time + ":00" : time}`;
|
||||||
|
return formatSafe(iso, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Fallback
|
||||||
return formatSafe(input, tz);
|
return formatSafe(input, tz);
|
||||||
|
|
||||||
function formatSafe(str, tz) {
|
function formatSafe(str, tz) {
|
||||||
@@ -440,6 +460,7 @@ function localizeTimestamp(input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Replaces double quotes within single-quoted strings, then converts all single quotes to double quotes,
|
* Replaces double quotes within single-quoted strings, then converts all single quotes to double quotes,
|
||||||
@@ -1622,7 +1643,6 @@ async function executeOnce() {
|
|||||||
|
|
||||||
if (!isAppInitialized()) {
|
if (!isAppInitialized()) {
|
||||||
try {
|
try {
|
||||||
console.log("HERE");
|
|
||||||
|
|
||||||
await waitForGraphQLServer(); // Wait for the server to start
|
await waitForGraphQLServer(); // Wait for the server to start
|
||||||
|
|
||||||
@@ -1630,7 +1650,7 @@ async function executeOnce() {
|
|||||||
await cacheSettings();
|
await cacheSettings();
|
||||||
await cacheStrings();
|
await cacheStrings();
|
||||||
|
|
||||||
console.log("✅ All AJAX callbacks have completed");
|
console.log("All AJAX callbacks have completed");
|
||||||
onAllCallsComplete();
|
onAllCallsComplete();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
|
|||||||
@@ -247,12 +247,19 @@ function showModalPopupForm(
|
|||||||
let settingsArray = [];
|
let settingsArray = [];
|
||||||
if (Array.isArray(popupFormJson)) {
|
if (Array.isArray(popupFormJson)) {
|
||||||
popupFormJson.forEach(field => {
|
popupFormJson.forEach(field => {
|
||||||
collectSetting(
|
const result = collectSetting(
|
||||||
`${parentSettingKey}_popupform`, // prefix
|
`${parentSettingKey}_popupform`, // prefix
|
||||||
field.function, // setCodeName
|
field.function, // setCodeName
|
||||||
field.type, // setType (object)
|
field.type, // setType (object)
|
||||||
settingsArray
|
settingsArray
|
||||||
);
|
);
|
||||||
|
settingsArray = result.settingsArray;
|
||||||
|
|
||||||
|
if (!result.dataIsValid) {
|
||||||
|
msg = getString("Gen_Invalid_Value") + ":" + result.failedSettingKey;
|
||||||
|
console.error(msg);
|
||||||
|
showModalOk("ERROR", msg);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -779,6 +779,7 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
|
|||||||
let getStringKey = "";
|
let getStringKey = "";
|
||||||
let onClick = "console.log('onClick - Not implemented');";
|
let onClick = "console.log('onClick - Not implemented');";
|
||||||
let onChange = "console.log('onChange - Not implemented');";
|
let onChange = "console.log('onChange - Not implemented');";
|
||||||
|
let focusout = "console.log('focusout - Not implemented');";
|
||||||
let customParams = "";
|
let customParams = "";
|
||||||
let customId = "";
|
let customId = "";
|
||||||
let columns = [];
|
let columns = [];
|
||||||
@@ -830,6 +831,9 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
|
|||||||
if (option.onChange) {
|
if (option.onChange) {
|
||||||
onChange = option.onChange;
|
onChange = option.onChange;
|
||||||
}
|
}
|
||||||
|
if (option.focusout) {
|
||||||
|
focusout = option.focusout;
|
||||||
|
}
|
||||||
if (option.customParams) {
|
if (option.customParams) {
|
||||||
customParams = option.customParams;
|
customParams = option.customParams;
|
||||||
}
|
}
|
||||||
@@ -867,7 +871,8 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
|
|||||||
customId,
|
customId,
|
||||||
columns,
|
columns,
|
||||||
base64Regex,
|
base64Regex,
|
||||||
elementOptionsBase64
|
elementOptionsBase64,
|
||||||
|
focusout
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1015,6 +1020,20 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
|
|||||||
|
|
||||||
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
||||||
|
|
||||||
|
// Check if validation failed
|
||||||
|
if (
|
||||||
|
$(`#${setCodeName}`)
|
||||||
|
&& $(`#${setCodeName}`).attr("data-is-valid")
|
||||||
|
&& $(`#${setCodeName}`).attr("data-is-valid") == 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"settingsArray": settingsArray,
|
||||||
|
"dataIsValid": false,
|
||||||
|
"failedSettingKey": setCodeName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const opts = handleElementOptions('none', elementOptions, transformers, val = "");
|
const opts = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||||
|
|
||||||
// Map of handlers
|
// Map of handlers
|
||||||
@@ -1084,7 +1103,11 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
|
|||||||
const value = handlers[handlerKey]();
|
const value = handlers[handlerKey]();
|
||||||
settingsArray.push([prefix, setCodeName, dataType, value]);
|
settingsArray.push([prefix, setCodeName, dataType, value]);
|
||||||
|
|
||||||
return settingsArray;
|
return {
|
||||||
|
"settingsArray": settingsArray,
|
||||||
|
"dataIsValid": true,
|
||||||
|
"failedSettingKey": ""
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1137,7 +1160,8 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
|||||||
customId,
|
customId,
|
||||||
columns,
|
columns,
|
||||||
base64Regex,
|
base64Regex,
|
||||||
elementOptionsBase64
|
elementOptionsBase64,
|
||||||
|
focusout
|
||||||
} = handleElementOptions(setKey, elementOptions, transformers, inVal);
|
} = handleElementOptions(setKey, elementOptions, transformers, inVal);
|
||||||
|
|
||||||
// Override value
|
// Override value
|
||||||
@@ -1160,6 +1184,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
|||||||
const addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
|
const addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
|
||||||
|
|
||||||
inputHtml += `<select onChange="settingsChanged();${onChange}"
|
inputHtml += `<select onChange="settingsChanged();${onChange}"
|
||||||
|
onfocusout="${focusout}"
|
||||||
my-data-type="${dataType}"
|
my-data-type="${dataType}"
|
||||||
my-editable="${editable}"
|
my-editable="${editable}"
|
||||||
class="form-control ${addCss} ${cssClasses}"
|
class="form-control ${addCss} ${cssClasses}"
|
||||||
@@ -1185,6 +1210,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
|||||||
inputHtml += `<input
|
inputHtml += `<input
|
||||||
class="${inputClass} ${cssClasses}"
|
class="${inputClass} ${cssClasses}"
|
||||||
onChange="settingsChanged();${onChange}"
|
onChange="settingsChanged();${onChange}"
|
||||||
|
onfocusout="${focusout}"
|
||||||
my-data-type="${dataType}"
|
my-data-type="${dataType}"
|
||||||
my-customparams="${customParams}"
|
my-customparams="${customParams}"
|
||||||
my-customid="${customId}"
|
my-customid="${customId}"
|
||||||
@@ -1216,6 +1242,8 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
|||||||
case 'textarea':
|
case 'textarea':
|
||||||
inputHtml += `<textarea
|
inputHtml += `<textarea
|
||||||
class="form-control input"
|
class="form-control input"
|
||||||
|
onChange="settingsChanged();${onChange}"
|
||||||
|
onfocusout="${focusout}"
|
||||||
my-customparams="${customParams}"
|
my-customparams="${customParams}"
|
||||||
my-customid="${customId}"
|
my-customid="${customId}"
|
||||||
my-originalSetKey="${originalSetKey}"
|
my-originalSetKey="${originalSetKey}"
|
||||||
|
|||||||
@@ -140,8 +140,11 @@ function validateRegex(elem) {
|
|||||||
// Validate against regex
|
// Validate against regex
|
||||||
if (regex.test(value)) {
|
if (regex.test(value)) {
|
||||||
iconSpan.html("<i class='fa fa-check'></i>");
|
iconSpan.html("<i class='fa fa-check'></i>");
|
||||||
|
inputElem.attr("data-is-valid", "1");
|
||||||
} else {
|
} else {
|
||||||
iconSpan.html("<i class='fa fa-xmark'></i>");
|
iconSpan.html("<i class='fa fa-xmark'></i>");
|
||||||
|
showModalOk('WARNING', getString("Gen_Invalid_Value"));
|
||||||
|
inputElem.attr("data-is-valid", "0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,8 @@
|
|||||||
customId,
|
customId,
|
||||||
columns,
|
columns,
|
||||||
base64Regex,
|
base64Regex,
|
||||||
elementOptionsBase64
|
elementOptionsBase64,
|
||||||
|
focusout
|
||||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||||
|
|
||||||
// render based on element type
|
// render based on element type
|
||||||
|
|||||||
@@ -445,8 +445,11 @@
|
|||||||
$('#showOfflineNumber').text(`(${offlineCount})`);
|
$('#showOfflineNumber').text(`(${offlineCount})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now apply UI filter based on toggles
|
// Now apply UI filter based on toggles (always keep root)
|
||||||
const filteredDevices = allDevices.filter(device => {
|
const filteredDevices = allDevices.filter(device => {
|
||||||
|
const isRoot = (device.devMac || '').toLowerCase() === 'internet';
|
||||||
|
|
||||||
|
if (isRoot) return true;
|
||||||
if (!showArchived && parseInt(device.devIsArchived) === 1) return false;
|
if (!showArchived && parseInt(device.devIsArchived) === 1) return false;
|
||||||
if (!showOffline && parseInt(device.devPresentLastScan) === 0) return false;
|
if (!showOffline && parseInt(device.devPresentLastScan) === 0) return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -521,12 +524,16 @@ function getChildren(node, list, path, visited = [])
|
|||||||
|
|
||||||
// Loop through all items to find children of the current node
|
// Loop through all items to find children of the current node
|
||||||
for (var i in list) {
|
for (var i in list) {
|
||||||
if (list[i].devParentMAC.toLowerCase() == node.devMac.toLowerCase() && !hiddenMacs.includes(list[i].devParentMAC)) {
|
const item = list[i];
|
||||||
|
const parentMac = item.devParentMAC || ""; // null-safe
|
||||||
|
const nodeMac = node.devMac || ""; // null-safe
|
||||||
|
|
||||||
|
if (parentMac != "" && parentMac.toLowerCase() == nodeMac.toLowerCase() && !hiddenMacs.includes(parentMac)) {
|
||||||
|
|
||||||
visibleNodesCount++;
|
visibleNodesCount++;
|
||||||
|
|
||||||
// Process children recursively, passing a copy of the visited list
|
// Process children recursively, passing a copy of the visited list
|
||||||
children.push(getChildren(list[i], list, path + ((path == "") ? "" : '|') + list[i].devParentMAC, visited));
|
children.push(getChildren(list[i], list, path + ((path == "") ? "" : '|') + parentMac, visited));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,14 +572,32 @@ function getChildren(node, list, path, visited = [])
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function getHierarchy()
|
function getHierarchy()
|
||||||
{
|
{
|
||||||
|
// reset counters before rebuilding the hierarchy
|
||||||
|
leafNodesCount = 0;
|
||||||
|
visibleNodesCount = 0;
|
||||||
|
parentNodesCount = 0;
|
||||||
|
|
||||||
|
let internetNode = null;
|
||||||
|
|
||||||
for(i in deviceListGlobal)
|
for(i in deviceListGlobal)
|
||||||
{
|
{
|
||||||
if(deviceListGlobal[i].devMac == 'Internet')
|
if(deviceListGlobal[i].devMac == 'Internet')
|
||||||
{
|
{
|
||||||
return (getChildren(deviceListGlobal[i], deviceListGlobal, ''))
|
internetNode = deviceListGlobal[i];
|
||||||
|
|
||||||
|
return (getChildren(internetNode, deviceListGlobal, ''))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!internetNode) {
|
||||||
|
showModalOk(
|
||||||
|
getString('Network_Configuration_Error'),
|
||||||
|
getString('Network_Root_Not_Configured')
|
||||||
|
);
|
||||||
|
console.error("getHierarchy(): Internet node not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
@@ -671,8 +696,6 @@ function handleNodeClick(el)
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
var myTree;
|
var myTree;
|
||||||
|
|
||||||
|
|
||||||
var emSize;
|
var emSize;
|
||||||
var nodeHeight;
|
var nodeHeight;
|
||||||
// var sizeCoefficient = 1.4
|
// var sizeCoefficient = 1.4
|
||||||
@@ -689,26 +712,26 @@ function emToPx(em, element) {
|
|||||||
|
|
||||||
function initTree(myHierarchy)
|
function initTree(myHierarchy)
|
||||||
{
|
{
|
||||||
// calculate the drawing area based on teh tree width and available screen size
|
if(myHierarchy && myHierarchy.type !== "")
|
||||||
|
{
|
||||||
|
// calculate the drawing area based on the tree width and available screen size
|
||||||
let baseFontSize = parseFloat($('html').css('font-size'));
|
let baseFontSize = parseFloat($('html').css('font-size'));
|
||||||
let treeAreaHeight = ($(window).height() - 155); ;
|
let treeAreaHeight = ($(window).height() - 155); ;
|
||||||
|
let minNodeWidth = 60 // min safe node width not breaking the tree
|
||||||
|
|
||||||
// calculate the font size of the leaf nodes to fit everything into the tree area
|
// calculate the font size of the leaf nodes to fit everything into the tree area
|
||||||
leafNodesCount == 0 ? 1 : leafNodesCount;
|
leafNodesCount == 0 ? 1 : leafNodesCount;
|
||||||
|
|
||||||
emSize = pxToEm((treeAreaHeight/(leafNodesCount)).toFixed(2));
|
emSize = pxToEm((treeAreaHeight/(leafNodesCount)).toFixed(2));
|
||||||
|
|
||||||
let screenWidthEm = pxToEm($('.networkTable').width()-15);
|
// let screenWidthEm = pxToEm($('.networkTable').width()-15);
|
||||||
|
let minTreeWidthPx = parentNodesCount * minNodeWidth;
|
||||||
|
let actualWidthPx = $('.networkTable').width() - 15;
|
||||||
|
|
||||||
// init the drawing area size
|
let finalWidthPx = Math.max(actualWidthPx, minTreeWidthPx);
|
||||||
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${emToPx(screenWidthEm)}px`)
|
|
||||||
|
|
||||||
if(myHierarchy.type == "")
|
// override original value
|
||||||
{
|
let screenWidthEm = pxToEm(finalWidthPx);
|
||||||
showModalOk(getString('Network_Configuration_Error'), getString('Network_Root_Not_Configured'))
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle canvas and node size if only a few nodes
|
// handle canvas and node size if only a few nodes
|
||||||
emSize > 1 ? emSize = 1 : emSize = emSize;
|
emSize > 1 ? emSize = 1 : emSize = emSize;
|
||||||
@@ -718,6 +741,12 @@ function initTree(myHierarchy)
|
|||||||
|
|
||||||
// handle if only a few nodes
|
// handle if only a few nodes
|
||||||
nodeWidthPx > 160 ? nodeWidthPx = 160 : nodeWidthPx = nodeWidthPx;
|
nodeWidthPx > 160 ? nodeWidthPx = 160 : nodeWidthPx = nodeWidthPx;
|
||||||
|
if (nodeWidthPx < minNodeWidth) nodeWidthPx = minNodeWidth; // minimum safe width
|
||||||
|
|
||||||
|
console.log("Calculated nodeWidthPx =", nodeWidthPx, "emSize =", emSize , " screenWidthEm:", screenWidthEm, " emToPx(screenWidthEm):" , emToPx(screenWidthEm));
|
||||||
|
|
||||||
|
// init the drawing area size
|
||||||
|
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${emToPx(screenWidthEm)}px`)
|
||||||
|
|
||||||
console.log(Treeviz);
|
console.log(Treeviz);
|
||||||
|
|
||||||
@@ -823,6 +852,10 @@ function initTree(myHierarchy)
|
|||||||
|
|
||||||
// hide spinning icon
|
// hide spinning icon
|
||||||
hideSpinner()
|
hideSpinner()
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
console.error("getHierarchy() not returning expected result");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ function saveSettings()
|
|||||||
|
|
||||||
// save to the file
|
// save to the file
|
||||||
$new_name = $config_file.'_'.$timestamp.'.backup';
|
$new_name = $config_file.'_'.$timestamp.'.backup';
|
||||||
$new_location = $configFolderPath.$new_name;
|
$new_location = $configFolderPath.'/'.$new_name;
|
||||||
|
|
||||||
if(file_exists( $fullConfPath) != 1)
|
if(file_exists( $fullConfPath) != 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "تصفية",
|
"Gen_Filter": "تصفية",
|
||||||
"Gen_Generate": "إنشاء",
|
"Gen_Generate": "إنشاء",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "قاعدة البيانات مقفلة",
|
"Gen_LockedDB": "قاعدة البيانات مقفلة",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "غير متصل",
|
"Gen_Offline": "غير متصل",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtrar",
|
"Gen_Filter": "Filtrar",
|
||||||
"Gen_Generate": "Generar",
|
"Gen_Generate": "Generar",
|
||||||
"Gen_InvalidMac": "Mac address invàlida.",
|
"Gen_InvalidMac": "Mac address invàlida.",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
|
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
|
||||||
"Gen_NetworkMask": "Màscara de xarxa",
|
"Gen_NetworkMask": "Màscara de xarxa",
|
||||||
"Gen_Offline": "Fora de línia",
|
"Gen_Offline": "Fora de línia",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtr",
|
"Gen_Filter": "Filtr",
|
||||||
"Gen_Generate": "Vygenerovat",
|
"Gen_Generate": "Vygenerovat",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "CHYBA - Databáze je možná zamčená - Zkontrolujte F12 -> Nástroje pro vývojáře -> Konzole. nebo to zkuste později.",
|
"Gen_LockedDB": "CHYBA - Databáze je možná zamčená - Zkontrolujte F12 -> Nástroje pro vývojáře -> Konzole. nebo to zkuste později.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
|
|||||||
@@ -315,6 +315,7 @@
|
|||||||
"Gen_Filter": "Filter",
|
"Gen_Filter": "Filter",
|
||||||
"Gen_Generate": "Generieren",
|
"Gen_Generate": "Generieren",
|
||||||
"Gen_InvalidMac": "Ungültige MAC-Adresse.",
|
"Gen_InvalidMac": "Ungültige MAC-Adresse.",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.",
|
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filter",
|
"Gen_Filter": "Filter",
|
||||||
"Gen_Generate": "Generate",
|
"Gen_Generate": "Generate",
|
||||||
"Gen_InvalidMac": "Invalid Mac address.",
|
"Gen_InvalidMac": "Invalid Mac address.",
|
||||||
|
"Gen_Invalid_Value": "An invalid value was entered",
|
||||||
"Gen_LockedDB": "ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.",
|
"Gen_LockedDB": "ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.",
|
||||||
"Gen_NetworkMask": "Network mask",
|
"Gen_NetworkMask": "Network mask",
|
||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
|
|||||||
@@ -313,6 +313,7 @@
|
|||||||
"Gen_Filter": "Filtro",
|
"Gen_Filter": "Filtro",
|
||||||
"Gen_Generate": "Generar",
|
"Gen_Generate": "Generar",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.",
|
"Gen_LockedDB": "Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Desconectado",
|
"Gen_Offline": "Desconectado",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "",
|
"Gen_Filter": "",
|
||||||
"Gen_Generate": "",
|
"Gen_Generate": "",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "",
|
"Gen_LockedDB": "",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "",
|
"Gen_Offline": "",
|
||||||
|
|||||||
1
front/php/templates/language/fr_fr.json
Executable file → Normal file
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtrer",
|
"Gen_Filter": "Filtrer",
|
||||||
"Gen_Generate": "Générer",
|
"Gen_Generate": "Générer",
|
||||||
"Gen_InvalidMac": "Adresse MAC invalide.",
|
"Gen_InvalidMac": "Adresse MAC invalide.",
|
||||||
|
"Gen_Invalid_Value": "Une valeur invalide a été renseignée",
|
||||||
"Gen_LockedDB": "Erreur - La base de données est peut-être verrouillée - Vérifier avec les outils de dév via F12 -> Console ou essayer plus tard.",
|
"Gen_LockedDB": "Erreur - La base de données est peut-être verrouillée - Vérifier avec les outils de dév via F12 -> Console ou essayer plus tard.",
|
||||||
"Gen_NetworkMask": "Masque réseau",
|
"Gen_NetworkMask": "Masque réseau",
|
||||||
"Gen_Offline": "Hors ligne",
|
"Gen_Offline": "Hors ligne",
|
||||||
|
|||||||
1
front/php/templates/language/it_it.json
Executable file → Normal file
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtro",
|
"Gen_Filter": "Filtro",
|
||||||
"Gen_Generate": "Genera",
|
"Gen_Generate": "Genera",
|
||||||
"Gen_InvalidMac": "Indirizzo Mac non valido.",
|
"Gen_InvalidMac": "Indirizzo Mac non valido.",
|
||||||
|
"Gen_Invalid_Value": "È stato inserito un valore non valido",
|
||||||
"Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.",
|
"Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.",
|
||||||
"Gen_NetworkMask": "Maschera di rete",
|
"Gen_NetworkMask": "Maschera di rete",
|
||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filter",
|
"Gen_Filter": "Filter",
|
||||||
"Gen_Generate": "",
|
"Gen_Generate": "",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "FEIL - DB kan være låst - Sjekk F12 Dev tools -> Konsoll eller prøv senere.",
|
"Gen_LockedDB": "FEIL - DB kan være låst - Sjekk F12 Dev tools -> Konsoll eller prøv senere.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Frakoblet",
|
"Gen_Offline": "Frakoblet",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtr",
|
"Gen_Filter": "Filtr",
|
||||||
"Gen_Generate": "Wygeneruj",
|
"Gen_Generate": "Wygeneruj",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "Błąd - Baza danych może być zablokowana - Sprawdź narzędzia deweloperskie F12 -> Konsola lub spróbuj później.",
|
"Gen_LockedDB": "Błąd - Baza danych może być zablokowana - Sprawdź narzędzia deweloperskie F12 -> Konsola lub spróbuj później.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Niedostępne",
|
"Gen_Offline": "Niedostępne",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtro",
|
"Gen_Filter": "Filtro",
|
||||||
"Gen_Generate": "Gerar",
|
"Gen_Generate": "Gerar",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "ERRO - O banco de dados pode estar bloqueado - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
"Gen_LockedDB": "ERRO - O banco de dados pode estar bloqueado - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Offline",
|
"Gen_Offline": "Offline",
|
||||||
|
|||||||
1
front/php/templates/language/pt_pt.json
Normal file → Executable file
@@ -310,6 +310,7 @@
|
|||||||
"Gen_Error": "Erro",
|
"Gen_Error": "Erro",
|
||||||
"Gen_Filter": "Filtro",
|
"Gen_Filter": "Filtro",
|
||||||
"Gen_Generate": "Gerar",
|
"Gen_Generate": "Gerar",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_InvalidMac": "Endereço MAC Inválido.",
|
"Gen_InvalidMac": "Endereço MAC Inválido.",
|
||||||
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
|
||||||
"Gen_NetworkMask": "Máscara de Rede",
|
"Gen_NetworkMask": "Máscara de Rede",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Фильтр",
|
"Gen_Filter": "Фильтр",
|
||||||
"Gen_Generate": "Генерировать",
|
"Gen_Generate": "Генерировать",
|
||||||
"Gen_InvalidMac": "Неверный Mac-адрес.",
|
"Gen_InvalidMac": "Неверный Mac-адрес.",
|
||||||
|
"Gen_Invalid_Value": "Введено некорректное значение",
|
||||||
"Gen_LockedDB": "ОШИБКА - Возможно, база данных заблокирована. Проверьте инструменты разработчика F12 -> Консоль или повторите попытку позже.",
|
"Gen_LockedDB": "ОШИБКА - Возможно, база данных заблокирована. Проверьте инструменты разработчика F12 -> Консоль или повторите попытку позже.",
|
||||||
"Gen_NetworkMask": "Маска сети",
|
"Gen_NetworkMask": "Маска сети",
|
||||||
"Gen_Offline": "Оффлайн",
|
"Gen_Offline": "Оффлайн",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "",
|
"Gen_Filter": "",
|
||||||
"Gen_Generate": "",
|
"Gen_Generate": "",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "",
|
"Gen_LockedDB": "",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "",
|
"Gen_Offline": "",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Filtre",
|
"Gen_Filter": "Filtre",
|
||||||
"Gen_Generate": "Oluştur",
|
"Gen_Generate": "Oluştur",
|
||||||
"Gen_InvalidMac": "",
|
"Gen_InvalidMac": "",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "HATA - Veritabanı kilitlenmiş olabilir - F12 Geliştirici araçlarını -> Konsol kısmını kontrol edin veya daha sonra tekrar deneyin.",
|
"Gen_LockedDB": "HATA - Veritabanı kilitlenmiş olabilir - F12 Geliştirici araçlarını -> Konsol kısmını kontrol edin veya daha sonra tekrar deneyin.",
|
||||||
"Gen_NetworkMask": "",
|
"Gen_NetworkMask": "",
|
||||||
"Gen_Offline": "Çevrimdışı",
|
"Gen_Offline": "Çevrimdışı",
|
||||||
|
|||||||
1
front/php/templates/language/uk_ua.json
Executable file → Normal file
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "Фільтр",
|
"Gen_Filter": "Фільтр",
|
||||||
"Gen_Generate": "Генерувати",
|
"Gen_Generate": "Генерувати",
|
||||||
"Gen_InvalidMac": "Недійсна Mac-адреса.",
|
"Gen_InvalidMac": "Недійсна Mac-адреса.",
|
||||||
|
"Gen_Invalid_Value": "Введено недійсне значення",
|
||||||
"Gen_LockedDB": "ПОМИЛКА – БД може бути заблоковано – перевірте F12 Інструменти розробника -> Консоль або спробуйте пізніше.",
|
"Gen_LockedDB": "ПОМИЛКА – БД може бути заблоковано – перевірте F12 Інструменти розробника -> Консоль або спробуйте пізніше.",
|
||||||
"Gen_NetworkMask": "Маска мережі",
|
"Gen_NetworkMask": "Маска мережі",
|
||||||
"Gen_Offline": "Офлайн",
|
"Gen_Offline": "Офлайн",
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
"Gen_Filter": "筛选",
|
"Gen_Filter": "筛选",
|
||||||
"Gen_Generate": "生成",
|
"Gen_Generate": "生成",
|
||||||
"Gen_InvalidMac": "无效的 Mac 地址。",
|
"Gen_InvalidMac": "无效的 Mac 地址。",
|
||||||
|
"Gen_Invalid_Value": "",
|
||||||
"Gen_LockedDB": "错误 - DB 可能被锁定 - 检查 F12 开发工具 -> 控制台或稍后重试。",
|
"Gen_LockedDB": "错误 - DB 可能被锁定 - 检查 F12 开发工具 -> 控制台或稍后重试。",
|
||||||
"Gen_NetworkMask": "网络掩码",
|
"Gen_NetworkMask": "网络掩码",
|
||||||
"Gen_Offline": "离线",
|
"Gen_Offline": "离线",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -377,7 +377,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -225,7 +225,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -185,7 +185,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -175,7 +175,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -596,7 +596,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -397,7 +397,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -180,7 +180,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -203,7 +203,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -468,7 +468,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -227,7 +227,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
|
||||||
import re
|
import re
|
||||||
import nmap
|
import nmap
|
||||||
|
|
||||||
@@ -17,6 +16,7 @@ from plugin_helper import Plugin_Objects # noqa: E402 [flake8 lint suppression]
|
|||||||
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
|
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
|
||||||
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
||||||
from const import logPath # noqa: E402 [flake8 lint suppression]
|
from const import logPath # noqa: E402 [flake8 lint suppression]
|
||||||
|
from utils.crypto_utils import string_to_mac_hash # noqa: E402 [flake8 lint suppression]
|
||||||
import conf # noqa: E402 [flake8 lint suppression]
|
import conf # noqa: E402 [flake8 lint suppression]
|
||||||
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
||||||
|
|
||||||
@@ -177,16 +177,6 @@ def parse_nmap_xml(xml_output, interface, fakeMac):
|
|||||||
return devices_list
|
return devices_list
|
||||||
|
|
||||||
|
|
||||||
def string_to_mac_hash(input_string):
|
|
||||||
# Calculate a hash using SHA-256
|
|
||||||
sha256_hash = hashlib.sha256(input_string.encode()).hexdigest()
|
|
||||||
|
|
||||||
# Take the first 12 characters of the hash and format as a MAC address
|
|
||||||
mac_hash = ':'.join(sha256_hash[i:i + 2] for i in range(0, 12, 2))
|
|
||||||
|
|
||||||
return mac_hash
|
|
||||||
|
|
||||||
|
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
# BEGIN
|
# BEGIN
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
|
|||||||
@@ -476,7 +476,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ The plugin connects to your Pi-hole’s API and retrieves:
|
|||||||
|
|
||||||
NetAlertX then uses this information to match or create devices in your system.
|
NetAlertX then uses this information to match or create devices in your system.
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> Some tip.
|
|
||||||
|
|
||||||
### Quick setup guide
|
### Quick setup guide
|
||||||
|
|
||||||
* You are running **Pi-hole v6** or newer.
|
* You are running **Pi-hole v6** or newer.
|
||||||
@@ -35,16 +32,8 @@ No additional Pi-hole configuration is required.
|
|||||||
| **PIHOLEAPI_SSL_VERIFY** | Whether to verify HTTPS certificates. Disable only for self-signed certificates. |
|
| **PIHOLEAPI_SSL_VERIFY** | Whether to verify HTTPS certificates. Disable only for self-signed certificates. |
|
||||||
| **PIHOLEAPI_RUN_TIMEOUT** | Request timeout in seconds. |
|
| **PIHOLEAPI_RUN_TIMEOUT** | Request timeout in seconds. |
|
||||||
| **PIHOLEAPI_API_MAXCLIENTS** | Maximum number of devices to request from Pi-hole. Defaults are usually fine. |
|
| **PIHOLEAPI_API_MAXCLIENTS** | Maximum number of devices to request from Pi-hole. Defaults are usually fine. |
|
||||||
|
| **PIHOLEAPI_FAKE_MAC** | Generate FAKE MAC from IP. |
|
||||||
|
|
||||||
### Example Configuration
|
|
||||||
|
|
||||||
| Setting Key | Sample Value |
|
|
||||||
| ---------------------------- | -------------------------------------------------- |
|
|
||||||
| **PIHOLEAPI_URL** | `http://pi.hole/` |
|
|
||||||
| **PIHOLEAPI_PASSWORD** | `passw0rd` |
|
|
||||||
| **PIHOLEAPI_SSL_VERIFY** | `true` |
|
|
||||||
| **PIHOLEAPI_RUN_TIMEOUT** | `30` |
|
|
||||||
| **PIHOLEAPI_API_MAXCLIENTS** | `500` |
|
|
||||||
|
|
||||||
### ⚠️ Troubleshooting
|
### ⚠️ Troubleshooting
|
||||||
|
|
||||||
@@ -110,6 +99,32 @@ Then re-run the plugin.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### ❌ Some devices are missing
|
||||||
|
|
||||||
|
Check:
|
||||||
|
|
||||||
|
* Pi-hole shows devices under **Settings → Network**
|
||||||
|
* NetAlertX logs contain:
|
||||||
|
|
||||||
|
```
|
||||||
|
[PIHOLEAPI] Skipping invalid MAC (see PIHOLEAPI_FAKE_MAC setting) ...
|
||||||
|
```
|
||||||
|
|
||||||
|
If devices are missing:
|
||||||
|
|
||||||
|
* The app skipps devices with invalid MACs
|
||||||
|
* Enable PIHOLEAPI_FAKE_MAC if you want to import these devices with a fake mac and you are not concerned with data inconsistencies later on
|
||||||
|
|
||||||
|
Try enabling PIHOLEAPI_FAKE_MAC:
|
||||||
|
|
||||||
|
```
|
||||||
|
PIHOLEAPI_FAKE_MAC = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Then re-run the plugin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#### ❌ Wrong or missing hostnames
|
#### ❌ Wrong or missing hostnames
|
||||||
|
|
||||||
Pi-hole only reports names it knows from:
|
Pi-hole only reports names it knows from:
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
@@ -279,6 +279,41 @@
|
|||||||
"string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
"string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "FAKE_MAC",
|
||||||
|
"type": {
|
||||||
|
"dataType": "boolean",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"type": "checkbox"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": false,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Fake MAC if empty"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Some PiHole devices don't have a MAC assigned. Enabling the FAKE_MAC setting generates a fake MAC address from the IP address to track devices, but it may cause inconsistencies if IPs change or devices are re-discovered with a different MAC. Static IPs are recommended. Device type and icon might not be detected correctly and some plugins might fail if they depend on a valid MAC address. When unchecked, devices with empty MAC addresses are skipped."
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"database_column_definitions": [
|
"database_column_definitions": [
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
|||||||
from const import logPath # noqa: E402 [flake8 lint suppression]
|
from const import logPath # noqa: E402 [flake8 lint suppression]
|
||||||
import conf # noqa: E402 [flake8 lint suppression]
|
import conf # noqa: E402 [flake8 lint suppression]
|
||||||
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
||||||
|
from utils.crypto_utils import string_to_mac_hash # noqa: E402 [flake8 lint suppression]
|
||||||
|
|
||||||
# Setup timezone & logger using standard NAX helpers
|
# Setup timezone & logger using standard NAX helpers
|
||||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||||
@@ -42,6 +43,7 @@ PIHOLEAPI_SES_CSRF = None
|
|||||||
PIHOLEAPI_API_MAXCLIENTS = None
|
PIHOLEAPI_API_MAXCLIENTS = None
|
||||||
PIHOLEAPI_VERIFY_SSL = True
|
PIHOLEAPI_VERIFY_SSL = True
|
||||||
PIHOLEAPI_RUN_TIMEOUT = 10
|
PIHOLEAPI_RUN_TIMEOUT = 10
|
||||||
|
PIHOLEAPI_FAKE_MAC = get_setting_value('PIHOLEAPI_FAKE_MAC')
|
||||||
VERSION_DATE = "NAX-PIHOLEAPI-1.0"
|
VERSION_DATE = "NAX-PIHOLEAPI-1.0"
|
||||||
|
|
||||||
|
|
||||||
@@ -222,8 +224,14 @@ def gather_device_entries():
|
|||||||
if ip in iplist:
|
if ip in iplist:
|
||||||
lastQuery = str(now_ts)
|
lastQuery = str(now_ts)
|
||||||
|
|
||||||
|
tmpMac = hwaddr.lower()
|
||||||
|
|
||||||
|
# ensure fake mac if enabled
|
||||||
|
if PIHOLEAPI_FAKE_MAC and is_mac(tmpMac) is False:
|
||||||
|
tmpMac = string_to_mac_hash(ip)
|
||||||
|
|
||||||
entries.append({
|
entries.append({
|
||||||
'mac': hwaddr.lower(),
|
'mac': tmpMac,
|
||||||
'ip': ip,
|
'ip': ip,
|
||||||
'name': name,
|
'name': name,
|
||||||
'macVendor': macVendor,
|
'macVendor': macVendor,
|
||||||
@@ -281,7 +289,7 @@ def main():
|
|||||||
foreignKey=str(entry['mac'])
|
foreignKey=str(entry['mac'])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
mylog('verbose', [f"[{pluginName}] Skipping invalid MAC: {entry['name']}|{entry['mac']}|{entry['ip']}"])
|
mylog('verbose', [f"[{pluginName}] Skipping invalid MAC (see PIHOLEAPI_FAKE_MAC setting): {entry['name']}|{entry['mac']}|{entry['ip']}"])
|
||||||
|
|
||||||
# Write result file for NetAlertX to ingest
|
# Write result file for NetAlertX to ingest
|
||||||
plugin_objects.write_result_file()
|
plugin_objects.write_result_file()
|
||||||
|
|||||||
@@ -182,7 +182,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ Specify the following settings in the Settings section of NetAlertX:
|
|||||||
|
|
||||||
If unsure, please check [snmpwalk examples](https://www.comparitech.com/net-admin/snmpwalk-examples-windows-linux/).
|
If unsure, please check [snmpwalk examples](https://www.comparitech.com/net-admin/snmpwalk-examples-windows-linux/).
|
||||||
|
|
||||||
|
Supported output formats:
|
||||||
|
|
||||||
|
```
|
||||||
|
ipNetToMediaPhysAddress[3][192.168.1.9] 6C:6C:6C:6C:6C:b6C1
|
||||||
|
IP-MIB::ipNetToMediaPhysAddress.17.10.10.3.202 = STRING: f8:81:1a:ef:ef:ef
|
||||||
|
mib-2.3.1.1.2.15.1.192.168.1.14 "2C F4 32 18 61 43 "
|
||||||
|
```
|
||||||
|
|
||||||
### Setup Cisco IOS
|
### Setup Cisco IOS
|
||||||
|
|
||||||
Enable IOS SNMP service and restrict to selected (internal) IP/Subnet.
|
Enable IOS SNMP service and restrict to selected (internal) IP/Subnet.
|
||||||
|
|||||||
@@ -512,7 +512,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
mylog('verbose', ['[SNMPDSC] In script '])
|
mylog('verbose', f"[{pluginName}] In script ")
|
||||||
|
|
||||||
# init global variables
|
# init global variables
|
||||||
global snmpWalkCmds
|
global snmpWalkCmds
|
||||||
@@ -57,7 +57,7 @@ def main():
|
|||||||
commands = [snmpWalkCmds]
|
commands = [snmpWalkCmds]
|
||||||
|
|
||||||
for cmd in commands:
|
for cmd in commands:
|
||||||
mylog('verbose', ['[SNMPDSC] Router snmpwalk command: ', cmd])
|
mylog('verbose', [f"[{pluginName}] Router snmpwalk command: ", cmd])
|
||||||
# split the string, remove white spaces around each item, and exclude any empty strings
|
# split the string, remove white spaces around each item, and exclude any empty strings
|
||||||
snmpwalkArgs = [arg.strip() for arg in cmd.split(' ') if arg.strip()]
|
snmpwalkArgs = [arg.strip() for arg in cmd.split(' ') if arg.strip()]
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ def main():
|
|||||||
timeout=(timeoutSetting)
|
timeout=(timeoutSetting)
|
||||||
)
|
)
|
||||||
|
|
||||||
mylog('verbose', ['[SNMPDSC] output: ', output])
|
mylog('verbose', [f"[{pluginName}] output: ", output])
|
||||||
|
|
||||||
lines = output.split('\n')
|
lines = output.split('\n')
|
||||||
|
|
||||||
@@ -80,6 +80,8 @@ def main():
|
|||||||
|
|
||||||
tmpSplt = line.split('"')
|
tmpSplt = line.split('"')
|
||||||
|
|
||||||
|
# Expected Format:
|
||||||
|
# mib-2.3.1.1.2.15.1.192.168.1.14 "2C F4 32 18 61 43 "
|
||||||
if len(tmpSplt) == 3:
|
if len(tmpSplt) == 3:
|
||||||
|
|
||||||
ipStr = tmpSplt[0].split('.')[-4:] # Get the last 4 elements to extract the IP
|
ipStr = tmpSplt[0].split('.')[-4:] # Get the last 4 elements to extract the IP
|
||||||
@@ -89,7 +91,7 @@ def main():
|
|||||||
macAddress = ':'.join(macStr)
|
macAddress = ':'.join(macStr)
|
||||||
ipAddress = '.'.join(ipStr)
|
ipAddress = '.'.join(ipStr)
|
||||||
|
|
||||||
mylog('verbose', [f'[SNMPDSC] IP: {ipAddress} MAC: {macAddress}'])
|
mylog('verbose', [f"[{pluginName}] IP: {ipAddress} MAC: {macAddress}"])
|
||||||
|
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
primaryId = handleEmpty(macAddress),
|
primaryId = handleEmpty(macAddress),
|
||||||
@@ -100,8 +102,40 @@ def main():
|
|||||||
foreignKey = handleEmpty(macAddress) # Use the primary ID as the foreign key
|
foreignKey = handleEmpty(macAddress) # Use the primary ID as the foreign key
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
mylog('verbose', ['[SNMPDSC] ipStr does not seem to contain a valid IP:', ipStr])
|
mylog('verbose', [f"[{pluginName}] ipStr does not seem to contain a valid IP:", ipStr])
|
||||||
|
|
||||||
|
# Expected Format:
|
||||||
|
# IP-MIB::ipNetToMediaPhysAddress.17.10.10.3.202 = STRING: f8:81:1a:ef:ef:ef
|
||||||
|
elif "ipNetToMediaPhysAddress" in line and "=" in line and "STRING:" in line:
|
||||||
|
|
||||||
|
# Split on "=" → ["IP-MIB::ipNetToMediaPhysAddress.xxx.xxx.xxx.xxx ", " STRING: aa:bb:cc:dd:ee:ff"]
|
||||||
|
left, right = line.split("=", 1)
|
||||||
|
|
||||||
|
# Extract the MAC (right side)
|
||||||
|
macAddress = right.split("STRING:")[-1].strip()
|
||||||
|
macAddress = normalize_mac(macAddress)
|
||||||
|
|
||||||
|
# Extract IP address from the left side
|
||||||
|
# tail of the OID: last 4 integers = IPv4 address
|
||||||
|
oid_parts = left.strip().split('.')
|
||||||
|
ip_parts = oid_parts[-4:]
|
||||||
|
ipAddress = ".".join(ip_parts)
|
||||||
|
|
||||||
|
mylog('verbose', [f"[{pluginName}] (fallback) IP: {ipAddress} MAC: {macAddress}"])
|
||||||
|
|
||||||
|
plugin_objects.add_object(
|
||||||
|
primaryId = handleEmpty(macAddress),
|
||||||
|
secondaryId = handleEmpty(ipAddress),
|
||||||
|
watched1 = '(unknown)',
|
||||||
|
watched2 = handleEmpty(snmpwalkArgs[6]),
|
||||||
|
extra = handleEmpty(line),
|
||||||
|
foreignKey = handleEmpty(macAddress)
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Expected Format:
|
||||||
|
# ipNetToMediaPhysAddress[3][192.168.1.9] 6C:6C:6C:6C:6C:b6C1
|
||||||
elif line.startswith('ipNetToMediaPhysAddress'):
|
elif line.startswith('ipNetToMediaPhysAddress'):
|
||||||
# Format: snmpwalk -OXsq output
|
# Format: snmpwalk -OXsq output
|
||||||
parts = line.split()
|
parts = line.split()
|
||||||
@@ -110,7 +144,7 @@ def main():
|
|||||||
ipAddress = parts[0].split('[')[-1][:-1]
|
ipAddress = parts[0].split('[')[-1][:-1]
|
||||||
macAddress = normalize_mac(parts[1])
|
macAddress = normalize_mac(parts[1])
|
||||||
|
|
||||||
mylog('verbose', [f'[SNMPDSC] IP: {ipAddress} MAC: {macAddress}'])
|
mylog('verbose', [f"[{pluginName}] IP: {ipAddress} MAC: {macAddress}"])
|
||||||
|
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
primaryId = handleEmpty(macAddress),
|
primaryId = handleEmpty(macAddress),
|
||||||
@@ -121,7 +155,7 @@ def main():
|
|||||||
foreignKey = handleEmpty(macAddress)
|
foreignKey = handleEmpty(macAddress)
|
||||||
)
|
)
|
||||||
|
|
||||||
mylog('verbose', ['[SNMPDSC] Entries found: ', len(plugin_objects)])
|
mylog('verbose', [f"[{pluginName}] Entries found: ", len(plugin_objects)])
|
||||||
|
|
||||||
plugin_objects.write_result_file()
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||
@@ -425,7 +425,7 @@
|
|||||||
"elementType": "input",
|
"elementType": "input",
|
||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{
|
{
|
||||||
"onChange": "validateRegex(this)"
|
"focusout": "validateRegex(this)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
|
||||||
|
|||||||