mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
11 Commits
00a47ab5d3
...
next_relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfd2cf9e20 | ||
|
|
61824abb9f | ||
|
|
33c5548fe1 | ||
|
|
fd41c395ae | ||
|
|
1a980844f0 | ||
|
|
82e018e284 | ||
|
|
e0e1233b1c | ||
|
|
74677f940e | ||
|
|
21a4d20579 | ||
|
|
9634e4e0f7 | ||
|
|
59b417705e |
12
.github/workflows/docker_prod.yml
vendored
12
.github/workflows/docker_prod.yml
vendored
@@ -32,6 +32,18 @@ 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
|
||||||
|
|||||||
1
.gitignore
vendored
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
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 && \
|
||||||
|
|||||||
@@ -253,11 +253,11 @@ docker run -it --rm --name netalertx --user "0" \
|
|||||||
ghcr.io/jokob-sk/netalertx:latest
|
ghcr.io/jokob-sk/netalertx:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
..or alternatively execute:
|
...or alternatively execute:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo chown -R 20211:20211 /local_data_dir
|
sudo chown -R 20211:20211 /local_data_dir
|
||||||
sudo chmod -R a+rwx /local_data_dir/
|
sudo chmod -R a+rwx /local_data_dir
|
||||||
```
|
```
|
||||||
|
|
||||||
7. Stop the container
|
7. Stop the container
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,39 +389,53 @@ 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 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;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 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 +454,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,
|
||||||
@@ -1629,7 +1644,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);
|
||||||
|
|||||||
@@ -521,13 +521,17 @@ 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
|
||||||
|
|
||||||
visibleNodesCount++;
|
if (parentMac != "" && parentMac.toLowerCase() == nodeMac.toLowerCase() && !hiddenMacs.includes(parentMac)) {
|
||||||
|
|
||||||
// Process children recursively, passing a copy of the visited list
|
visibleNodesCount++;
|
||||||
children.push(getChildren(list[i], list, path + ((path == "") ? "" : '|') + list[i].devParentMAC, visited));
|
|
||||||
}
|
// Process children recursively, passing a copy of the visited list
|
||||||
|
children.push(getChildren(list[i], list, path + ((path == "") ? "" : '|') + parentMac, visited));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track leaf and parent node counts
|
// Track leaf and parent node counts
|
||||||
@@ -565,14 +569,27 @@ function getChildren(node, list, path, visited = [])
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function getHierarchy()
|
function getHierarchy()
|
||||||
{
|
{
|
||||||
|
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 +688,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,140 +704,139 @@ 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 !== "")
|
||||||
|
|
||||||
let baseFontSize = parseFloat($('html').css('font-size'));
|
|
||||||
let treeAreaHeight = ($(window).height() - 155); ;
|
|
||||||
// calculate the font size of the leaf nodes to fit everything into the tree area
|
|
||||||
leafNodesCount == 0 ? 1 : leafNodesCount;
|
|
||||||
|
|
||||||
emSize = pxToEm((treeAreaHeight/(leafNodesCount)).toFixed(2));
|
|
||||||
|
|
||||||
let screenWidthEm = pxToEm($('.networkTable').width()-15);
|
|
||||||
|
|
||||||
// init the drawing area size
|
|
||||||
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${emToPx(screenWidthEm)}px`)
|
|
||||||
|
|
||||||
if(myHierarchy.type == "")
|
|
||||||
{
|
{
|
||||||
showModalOk(getString('Network_Configuration_Error'), getString('Network_Root_Not_Configured'))
|
// calculate the drawing area based on the tree width and available screen size
|
||||||
|
let baseFontSize = parseFloat($('html').css('font-size'));
|
||||||
|
let treeAreaHeight = ($(window).height() - 155); ;
|
||||||
|
|
||||||
return;
|
// calculate the font size of the leaf nodes to fit everything into the tree area
|
||||||
}
|
leafNodesCount == 0 ? 1 : leafNodesCount;
|
||||||
|
|
||||||
// handle canvas and node size if only a few nodes
|
emSize = pxToEm((treeAreaHeight/(leafNodesCount)).toFixed(2));
|
||||||
emSize > 1 ? emSize = 1 : emSize = emSize;
|
|
||||||
|
|
||||||
let nodeHeightPx = emToPx(emSize*1);
|
let screenWidthEm = pxToEm($('.networkTable').width()-15);
|
||||||
let nodeWidthPx = emToPx(screenWidthEm / (parentNodesCount));
|
|
||||||
|
|
||||||
// handle if only a few nodes
|
// init the drawing area size
|
||||||
nodeWidthPx > 160 ? nodeWidthPx = 160 : nodeWidthPx = nodeWidthPx;
|
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${emToPx(screenWidthEm)}px`)
|
||||||
|
|
||||||
console.log(Treeviz);
|
// handle canvas and node size if only a few nodes
|
||||||
|
emSize > 1 ? emSize = 1 : emSize = emSize;
|
||||||
|
|
||||||
myTree = Treeviz.create({
|
let nodeHeightPx = emToPx(emSize*1);
|
||||||
htmlId: "networkTree",
|
let nodeWidthPx = emToPx(screenWidthEm / (parentNodesCount));
|
||||||
renderNode: nodeData => {
|
|
||||||
|
|
||||||
(!emptyArr.includes(nodeData.data.port )) ? port = nodeData.data.port : port = "";
|
// handle if only a few nodes
|
||||||
|
nodeWidthPx > 160 ? nodeWidthPx = 160 : nodeWidthPx = nodeWidthPx;
|
||||||
|
|
||||||
(port == "" || port == 0 || port == 'None' ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
|
console.log(Treeviz);
|
||||||
|
|
||||||
portHtml = (port == "" || port == 0 || port == 'None' ) ? "   " : port;
|
myTree = Treeviz.create({
|
||||||
|
htmlId: "networkTree",
|
||||||
|
renderNode: nodeData => {
|
||||||
|
|
||||||
// Build HTML for individual nodes in the network diagram
|
(!emptyArr.includes(nodeData.data.port )) ? port = nodeData.data.port : port = "";
|
||||||
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ?
|
|
||||||
`<div class="netIcon">
|
|
||||||
${atob(nodeData.data.icon)}
|
|
||||||
</div>` : "";
|
|
||||||
devicePort = `<div class="netPort"
|
|
||||||
style="width:${emSize}em;height:${emSize}em">
|
|
||||||
${portHtml}</div>
|
|
||||||
<div class="portBckgIcon"
|
|
||||||
style="margin-left:-${emSize*0.7}em;">
|
|
||||||
${portBckgIcon}
|
|
||||||
</div>`;
|
|
||||||
collapseExpandIcon = nodeData.data.hiddenChildren ?
|
|
||||||
"square-plus" : "square-minus";
|
|
||||||
|
|
||||||
// generate +/- icon if node has children nodes
|
(port == "" || port == 0 || port == 'None' ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
|
||||||
collapseExpandHtml = nodeData.data.hasChildren ?
|
|
||||||
`<div class="netCollapse"
|
|
||||||
style="font-size:${nodeHeightPx/2}px;top:${Math.floor(nodeHeightPx / 4)}px"
|
|
||||||
data-mytreepath="${nodeData.data.path}"
|
|
||||||
data-mytreemac="${nodeData.data.mac}">
|
|
||||||
<i class="fa fa-${collapseExpandIcon} pointer"></i>
|
|
||||||
</div>` : "";
|
|
||||||
|
|
||||||
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
|
portHtml = (port == "" || port == 0 || port == 'None' ) ? "   " : port;
|
||||||
|
|
||||||
highlightedCss = nodeData.data.mac == selectedNodeMac ?
|
// Build HTML for individual nodes in the network diagram
|
||||||
" highlightedNode " : "";
|
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ?
|
||||||
cssNodeType = nodeData.data.devIsNetworkNodeDynamic ?
|
`<div class="netIcon">
|
||||||
" node-network-device " : " node-standard-device ";
|
${atob(nodeData.data.icon)}
|
||||||
|
</div>` : "";
|
||||||
|
devicePort = `<div class="netPort"
|
||||||
|
style="width:${emSize}em;height:${emSize}em">
|
||||||
|
${portHtml}</div>
|
||||||
|
<div class="portBckgIcon"
|
||||||
|
style="margin-left:-${emSize*0.7}em;">
|
||||||
|
${portBckgIcon}
|
||||||
|
</div>`;
|
||||||
|
collapseExpandIcon = nodeData.data.hiddenChildren ?
|
||||||
|
"square-plus" : "square-minus";
|
||||||
|
|
||||||
networkHardwareIcon = nodeData.data.devIsNetworkNodeDynamic ? `<span class="network-hw-icon">
|
// generate +/- icon if node has children nodes
|
||||||
<i class="fa-solid fa-hard-drive"></i>
|
collapseExpandHtml = nodeData.data.hasChildren ?
|
||||||
</span>` : "";
|
`<div class="netCollapse"
|
||||||
|
style="font-size:${nodeHeightPx/2}px;top:${Math.floor(nodeHeightPx / 4)}px"
|
||||||
|
data-mytreepath="${nodeData.data.path}"
|
||||||
|
data-mytreemac="${nodeData.data.mac}">
|
||||||
|
<i class="fa fa-${collapseExpandIcon} pointer"></i>
|
||||||
|
</div>` : "";
|
||||||
|
|
||||||
const badgeConf = getStatusBadgeParts(nodeData.data.presentLastScan, nodeData.data.alertDown, nodeData.data.mac, statusText = '')
|
selectedNodeMac = $(".nav-tabs-custom .active a").attr('data-mytabmac')
|
||||||
|
|
||||||
return result = `<div
|
highlightedCss = nodeData.data.mac == selectedNodeMac ?
|
||||||
class="node-inner hover-node-info box pointer ${highlightedCss} ${cssNodeType}"
|
" highlightedNode " : "";
|
||||||
style="height:${nodeHeightPx}px;font-size:${nodeHeightPx-5}px;"
|
cssNodeType = nodeData.data.devIsNetworkNodeDynamic ?
|
||||||
onclick="handleNodeClick(this)"
|
" node-network-device " : " node-standard-device ";
|
||||||
data-mac="${nodeData.data.mac}"
|
|
||||||
data-parentMac="${nodeData.data.parentMac}"
|
networkHardwareIcon = nodeData.data.devIsNetworkNodeDynamic ? `<span class="network-hw-icon">
|
||||||
data-name="${nodeData.data.name}"
|
<i class="fa-solid fa-hard-drive"></i>
|
||||||
data-ip="${nodeData.data.ip}"
|
</span>` : "";
|
||||||
data-mac="${nodeData.data.mac}"
|
|
||||||
data-vendor="${nodeData.data.vendor}"
|
const badgeConf = getStatusBadgeParts(nodeData.data.presentLastScan, nodeData.data.alertDown, nodeData.data.mac, statusText = '')
|
||||||
data-type="${nodeData.data.type}"
|
|
||||||
data-devIsNetworkNodeDynamic="${nodeData.data.devIsNetworkNodeDynamic}"
|
return result = `<div
|
||||||
data-lastseen="${nodeData.data.lastseen}"
|
class="node-inner hover-node-info box pointer ${highlightedCss} ${cssNodeType}"
|
||||||
data-firstseen="${nodeData.data.firstseen}"
|
style="height:${nodeHeightPx}px;font-size:${nodeHeightPx-5}px;"
|
||||||
data-relationship="${nodeData.data.relType}"
|
onclick="handleNodeClick(this)"
|
||||||
data-status="${nodeData.data.status}"
|
data-mac="${nodeData.data.mac}"
|
||||||
data-present="${nodeData.data.presentLastScan}"
|
data-parentMac="${nodeData.data.parentMac}"
|
||||||
data-alert="${nodeData.data.alertDown}"
|
data-name="${nodeData.data.name}"
|
||||||
data-icon="${nodeData.data.icon}"
|
data-ip="${nodeData.data.ip}"
|
||||||
>
|
data-mac="${nodeData.data.mac}"
|
||||||
<div class="netNodeText">
|
data-vendor="${nodeData.data.vendor}"
|
||||||
<strong><span>${devicePort} <span class="${badgeConf.cssText}">${deviceIcon}</span></span>
|
data-type="${nodeData.data.type}"
|
||||||
<span class="spanNetworkTree anonymizeDev" style="width:${nodeWidthPx-50}px">${nodeData.data.name}</span>
|
data-devIsNetworkNodeDynamic="${nodeData.data.devIsNetworkNodeDynamic}"
|
||||||
${networkHardwareIcon}
|
data-lastseen="${nodeData.data.lastseen}"
|
||||||
</strong>
|
data-firstseen="${nodeData.data.firstseen}"
|
||||||
|
data-relationship="${nodeData.data.relType}"
|
||||||
|
data-status="${nodeData.data.status}"
|
||||||
|
data-present="${nodeData.data.presentLastScan}"
|
||||||
|
data-alert="${nodeData.data.alertDown}"
|
||||||
|
data-icon="${nodeData.data.icon}"
|
||||||
|
>
|
||||||
|
<div class="netNodeText">
|
||||||
|
<strong><span>${devicePort} <span class="${badgeConf.cssText}">${deviceIcon}</span></span>
|
||||||
|
<span class="spanNetworkTree anonymizeDev" style="width:${nodeWidthPx-50}px">${nodeData.data.name}</span>
|
||||||
|
${networkHardwareIcon}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
${collapseExpandHtml}`;
|
||||||
${collapseExpandHtml}`;
|
},
|
||||||
},
|
mainAxisNodeSpacing: 'auto',
|
||||||
mainAxisNodeSpacing: 'auto',
|
// secondaryAxisNodeSpacing: 0.3,
|
||||||
// secondaryAxisNodeSpacing: 0.3,
|
nodeHeight: nodeHeightPx,
|
||||||
nodeHeight: nodeHeightPx,
|
nodeWidth: nodeWidthPx,
|
||||||
nodeWidth: nodeWidthPx,
|
marginTop: '5',
|
||||||
marginTop: '5',
|
isHorizontal : true,
|
||||||
isHorizontal : true,
|
hasZoom: true,
|
||||||
hasZoom: true,
|
hasPan: true,
|
||||||
hasPan: true,
|
marginLeft: '10',
|
||||||
marginLeft: '10',
|
marginRight: '10',
|
||||||
marginRight: '10',
|
idKey: "mac",
|
||||||
idKey: "mac",
|
hasFlatData: false,
|
||||||
hasFlatData: false,
|
relationnalField: "children",
|
||||||
relationnalField: "children",
|
linkWidth: (nodeData) => 2,
|
||||||
linkWidth: (nodeData) => 2,
|
linkColor: (nodeData) => {
|
||||||
linkColor: (nodeData) => {
|
relConf = getRelationshipConf(nodeData.data.relType)
|
||||||
relConf = getRelationshipConf(nodeData.data.relType)
|
return relConf.color;
|
||||||
return relConf.color;
|
}
|
||||||
}
|
// onNodeClick: (nodeData) => handleNodeClick(nodeData),
|
||||||
// onNodeClick: (nodeData) => handleNodeClick(nodeData),
|
});
|
||||||
});
|
|
||||||
|
|
||||||
console.log(deviceListGlobal);
|
console.log(deviceListGlobal);
|
||||||
myTree.refresh(myHierarchy);
|
myTree.refresh(myHierarchy);
|
||||||
|
|
||||||
// hide spinning icon
|
// hide spinning icon
|
||||||
hideSpinner()
|
hideSpinner()
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
console.error("getHierarchy() not returning expected result");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ if ! awk '$2 == "/" && $4 ~ /ro/ {found=1} END {exit !found}' /proc/mounts; then
|
|||||||
══════════════════════════════════════════════════════════════════════════════
|
══════════════════════════════════════════════════════════════════════════════
|
||||||
⚠️ Warning: Container is running as read-write, not in read-only mode.
|
⚠️ Warning: Container is running as read-write, not in read-only mode.
|
||||||
|
|
||||||
Please mount the root filesystem as --read-only or use read-only: true
|
Please mount the root filesystem as --read-only or use read_only: true
|
||||||
https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/read-only-filesystem.md
|
https://github.com/jokob-sk/NetAlertX/blob/main/docs/docker-troubleshooting/read-only-filesystem.md
|
||||||
══════════════════════════════════════════════════════════════════════════════
|
══════════════════════════════════════════════════════════════════════════════
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user