mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-30 23:03:03 -07:00
Merge netalertx/main into openapi-mcp-improvements
This commit is contained in:
@@ -5,7 +5,64 @@
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "INTERNET", "vendor": "" }
|
||||
],
|
||||
"name_pattern": []
|
||||
"name_pattern": [],
|
||||
"ip_pattern": [
|
||||
"^192\\.168\\.1\\.1$",
|
||||
"^192\\.168\\.0\\.1$",
|
||||
"^10\\.0\\.0\\.1$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Switch",
|
||||
"icon_html": "<i class=\"fa-solid fa-toggle-on\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "003192", "vendor": "TP-Link" },
|
||||
{ "mac_prefix": "50C7BF", "vendor": "TP-Link" },
|
||||
{ "mac_prefix": "B04E26", "vendor": "TP-Link" }
|
||||
],
|
||||
"name_pattern": ["hs200", "hs210", "hs220", "ks230", "smart switch", "light switch", "wall switch"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Plug",
|
||||
"icon_html": "<i class=\"fa-solid fa-plug\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "2887BA", "vendor": "TP-Link" }
|
||||
],
|
||||
"name_pattern": ["kp115", "hs100", "hs103", "hs105", "smart plug", "outlet", "plug"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Speaker",
|
||||
"icon_html": "<i class=\"fa fa-volume-up\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "14C14E", "vendor": "Google" },
|
||||
{ "mac_prefix": "44650D", "vendor": "Amazon" },
|
||||
{ "mac_prefix": "74ACB9", "vendor": "Google" }
|
||||
],
|
||||
"name_pattern": ["echo", "alexa", "dot", "nest-audio", "nest-mini", "google-home"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Appliance",
|
||||
"icon_html": "<i class=\"fa-solid fa-wind\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "446FF8", "vendor": "Dyson" }
|
||||
],
|
||||
"name_pattern": ["dyson", "purifier", "humidifier", "fan"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Home",
|
||||
"icon_html": "<i class=\"fa fa-house\"></i>",
|
||||
"matching_pattern": [],
|
||||
"name_pattern": ["google", "chromecast", "nest", "hub"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Phone",
|
||||
"icon_html": "<i class=\"fa-solid fa-mobile\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "001A79", "vendor": "Apple" },
|
||||
{ "mac_prefix": "B0BE83", "vendor": "Samsung" },
|
||||
{ "mac_prefix": "BC926B", "vendor": "Motorola" }
|
||||
],
|
||||
"name_pattern": ["iphone", "ipad", "pixel", "galaxy", "redmi", "android", "samsung"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Access Point",
|
||||
@@ -16,24 +73,7 @@
|
||||
{ "mac_prefix": "F4F5D8", "vendor": "TP-Link" },
|
||||
{ "mac_prefix": "F88E85", "vendor": "Netgear" }
|
||||
],
|
||||
"name_pattern": ["router", "gateway", "ap", "access point", "access-point", "switch"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Phone",
|
||||
"icon_html": "<i class=\"fa-brands fa-apple\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "001A79", "vendor": "Apple" },
|
||||
{ "mac_prefix": "B0BE83", "vendor": "Samsung" },
|
||||
{ "mac_prefix": "BC926B", "vendor": "Motorola" }
|
||||
],
|
||||
"name_pattern": ["iphone", "ipad", "pixel", "galaxy", "redmi"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Phone",
|
||||
"icon_html": "<i class=\"fa-solid fa-mobile\"></i>",
|
||||
"matching_pattern": [
|
||||
],
|
||||
"name_pattern": ["android","samsung"]
|
||||
"name_pattern": ["router", "gateway", "ap", "access point", "access-point", "switch", "sg105", "sg108", "managed switch", "unmanaged switch", "poe switch", "ethernet switch"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Tablet",
|
||||
@@ -43,25 +83,19 @@
|
||||
{ "mac_prefix": "BC4C4C", "vendor": "Samsung" }
|
||||
],
|
||||
"name_pattern": ["tablet", "pad"]
|
||||
},
|
||||
{
|
||||
"dev_type": "IoT",
|
||||
"icon_html": "<i class=\"fa-brands fa-raspberry-pi\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "B827EB", "vendor": "Raspberry Pi" },
|
||||
{ "mac_prefix": "DCA632", "vendor": "Raspberry Pi" }
|
||||
],
|
||||
"name_pattern": ["raspberry", "pi"]
|
||||
},
|
||||
{
|
||||
"dev_type": "IoT",
|
||||
"icon_html": "<i class=\"fa-solid fa-microchip\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "B827EB", "vendor": "Raspberry Pi" },
|
||||
{ "mac_prefix": "DCA632", "vendor": "Raspberry Pi" },
|
||||
{ "mac_prefix": "840D8E", "vendor": "Espressif" },
|
||||
{ "mac_prefix": "ECFABC", "vendor": "Espressif" },
|
||||
{ "mac_prefix": "7C9EBD", "vendor": "Espressif" }
|
||||
{ "mac_prefix": "7C9EBD", "vendor": "Espressif" },
|
||||
{ "mac_prefix": "286DCD", "vendor": "Beijing Winner Microelectronics" }
|
||||
],
|
||||
"name_pattern": ["raspberry", "pi"]
|
||||
"name_pattern": ["raspberry", "pi", "thingsturn", "w600", "w601"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Desktop",
|
||||
@@ -69,9 +103,11 @@
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "001422", "vendor": "Dell" },
|
||||
{ "mac_prefix": "001874", "vendor": "Lenovo" },
|
||||
{ "mac_prefix": "00E04C", "vendor": "Hewlett Packard" }
|
||||
{ "mac_prefix": "00E04C", "vendor": "Hewlett Packard" },
|
||||
{ "mac_prefix": "F44D30", "vendor": "Elitegroup Computer Systems" },
|
||||
{ "mac_prefix": "1C697A", "vendor": "Elitegroup Computer Systems" }
|
||||
],
|
||||
"name_pattern": ["desktop", "pc", "computer"]
|
||||
"name_pattern": ["desktop", "pc", "computer", "liva", "ecs"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Laptop",
|
||||
@@ -80,9 +116,10 @@
|
||||
{ "mac_prefix": "3C0754", "vendor": "HP" },
|
||||
{ "mac_prefix": "0017A4", "vendor": "Dell" },
|
||||
{ "mac_prefix": "F4CE46", "vendor": "Lenovo" },
|
||||
{ "mac_prefix": "409F38", "vendor": "Acer" }
|
||||
{ "mac_prefix": "409F38", "vendor": "Acer" },
|
||||
{ "mac_prefix": "9CB6D0", "vendor": "Rivet Networks" }
|
||||
],
|
||||
"name_pattern": ["macbook", "imac", "laptop", "notebook"]
|
||||
"name_pattern": ["macbook", "imac", "laptop", "notebook", "alienware", "razer", "msi"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Server",
|
||||
@@ -123,9 +160,10 @@
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "001FA7", "vendor": "Sony" },
|
||||
{ "mac_prefix": "7C04D0", "vendor": "Nintendo" },
|
||||
{ "mac_prefix": "EC26CA", "vendor": "Sony" }
|
||||
{ "mac_prefix": "EC26CA", "vendor": "Sony" },
|
||||
{ "mac_prefix": "48B02D", "vendor": "NVIDIA" }
|
||||
],
|
||||
"name_pattern": ["playstation", "xbox"]
|
||||
"name_pattern": ["playstation", "xbox", "shield", "nvidia"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Camera",
|
||||
@@ -138,15 +176,6 @@
|
||||
],
|
||||
"name_pattern": ["camera", "cam", "webcam"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Speaker",
|
||||
"icon_html": "<i class=\"fa fa-volume-up\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "44650D", "vendor": "Amazon" },
|
||||
{ "mac_prefix": "74ACB9", "vendor": "Google" }
|
||||
],
|
||||
"name_pattern": ["echo", "alexa", "dot"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Router",
|
||||
"icon_html": "<i class=\"fa fa-random\"></i>",
|
||||
@@ -154,23 +183,13 @@
|
||||
{ "mac_prefix": "000C29", "vendor": "Cisco" },
|
||||
{ "mac_prefix": "00155D", "vendor": "MikroTik" }
|
||||
],
|
||||
"name_pattern": ["router", "gateway", "ap", "access point", "access-point"],
|
||||
"ip_pattern": [
|
||||
"^192\\.168\\.[0-1]\\.1$",
|
||||
"^10\\.0\\.0\\.1$"
|
||||
]
|
||||
"name_pattern": ["router", "gateway", "ap", "access point"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Light",
|
||||
"icon_html": "<i class=\"fa fa-lightbulb\"></i>",
|
||||
"matching_pattern": [],
|
||||
"name_pattern": ["hue", "lifx", "bulb"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Home",
|
||||
"icon_html": "<i class=\"fa fa-house\"></i>",
|
||||
"matching_pattern": [],
|
||||
"name_pattern": ["google", "chromecast", "nest"]
|
||||
"name_pattern": ["hue", "lifx", "bulb", "light"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smartwatch",
|
||||
@@ -187,14 +206,9 @@
|
||||
{
|
||||
"dev_type": "Security Device",
|
||||
"icon_html": "<i class=\"fa fa-shield-alt\"></i>",
|
||||
"matching_pattern": [],
|
||||
"name_pattern": ["doorbell", "lock", "security"]
|
||||
},
|
||||
{
|
||||
"dev_type": "Smart Light",
|
||||
"icon_html": "<i class=\"fa-solid fa-lightbulb\"></i>",
|
||||
"matching_pattern": [
|
||||
{ "mac_prefix": "047BCB", "vendor": "Universal Global Scientific" }
|
||||
],
|
||||
"name_pattern": ["light","bulb"]
|
||||
"name_pattern": ["doorbell", "lock", "security", "mmd-", "ring"]
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -138,36 +138,6 @@ The Device Edit form displays lock/unlock buttons for all tracked fields:
|
||||
2. **Unlock Button** (🔓): Click to allow plugin overwrites again
|
||||
3. **Source Indicator**: Shows current field source (USER, LOCKED, NEWDEV, or plugin name)
|
||||
|
||||
## UI Workflow
|
||||
|
||||
### Locking a Field via UI
|
||||
|
||||
1. Navigate to Device Details
|
||||
2. Find the field you want to protect
|
||||
3. Click the lock button (🔒) next to the field
|
||||
4. Button changes to unlock (🔓) and source indicator turns red (LOCKED)
|
||||
5. Field is now protected from plugin overwrites
|
||||
|
||||
### Unlocking a Field via UI
|
||||
|
||||
1. Find the locked field (button shows 🔓)
|
||||
2. Click the unlock button
|
||||
3. Button changes back to lock (🔒) and source resets to NEWDEV
|
||||
4. Plugins can now update this field again
|
||||
|
||||
## Authorization
|
||||
|
||||
All lock/unlock operations require:
|
||||
- Valid API token in `Authorization: Bearer {token}` header
|
||||
- User must be authenticated to the NetAlertX instance
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Backend Logic
|
||||
The lock/unlock feature is implemented in:
|
||||
- **API Endpoint**: `/server/api_server/api_server_start.py` - `api_device_field_lock()`
|
||||
- **Data Model**: `/server/models/device_instance.py` - Authorization checks in `setDeviceData()`
|
||||
- **Database**: Devices table with `*Source` columns tracking field origins
|
||||
|
||||
### Authorization Handler
|
||||
|
||||
@@ -179,6 +149,9 @@ The authoritative field update logic prevents plugin overwrites:
|
||||
4. If source is `NEWDEV` or plugin name, plugin update is accepted
|
||||
|
||||
## See Also
|
||||
|
||||
- [Device locking](./DEVICE_FIELD_LOCK.md)
|
||||
- [Device source fields](./DEVICE_SOURCE_FIELDS.md)
|
||||
- [API Device Endpoints Documentation](./API_DEVICE.md)
|
||||
- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields)
|
||||
- [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md)
|
||||
|
||||
@@ -37,7 +37,12 @@ Each locked field has a "source" indicator that shows you why the value is prote
|
||||
| 📡 **NEWDEV** | Default/unset value | Yes, plugins can update |
|
||||
| 📡 **Plugin name** | Last updated by a plugin (e.g., UNIFIAPI) | Yes, plugins can update if field in SET_ALWAYS |
|
||||
|
||||
## How to Use
|
||||
Overwrite rules are
|
||||
|
||||
> [!TIP]
|
||||
> You can bulk-unlock devices in the [Multi-edit](./DEVICES_BULK_EDITING.md) dialog. This removes all `USER` and `LOCKED` values from all `*Source` fields of selected devices.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Lock a Field (Prevent Plugin Changes)
|
||||
|
||||
@@ -147,13 +152,13 @@ Each locked field has a "source" indicator that shows you why the value is prote
|
||||
- Check if you accidentally unlocked it
|
||||
- Open an issue if it persists
|
||||
|
||||
## For More Information
|
||||
## See also
|
||||
|
||||
- **Technical details:** See [API_DEVICE_FIELD_LOCK.md](API_DEVICE_FIELD_LOCK.md)
|
||||
- **Plugin configuration:** See [PLUGINS_DEV_CONFIG.md](PLUGINS_DEV_CONFIG.md)
|
||||
- **Admin guide:** See [DEVICE_MANAGEMENT.md](DEVICE_MANAGEMENT.md)
|
||||
|
||||
---
|
||||
|
||||
**Quick Start:** Find a device field you want to protect → Click the lock icon → That's it! The field won't change until you unlock it.
|
||||
- [Device locking](./DEVICE_FIELD_LOCK.md)
|
||||
- [Device source fields](./DEVICE_SOURCE_FIELDS.md)
|
||||
- [API Device Endpoints Documentation](./API_DEVICE.md)
|
||||
- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields)
|
||||
- [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md)
|
||||
- [Device locking APIs](API_DEVICE_FIELD_LOCK.md)
|
||||
- [Device management](DEVICE_MANAGEMENT.md)
|
||||
|
||||
|
||||
67
docs/DEVICE_SOURCE_FIELDS.md
Normal file
67
docs/DEVICE_SOURCE_FIELDS.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Understanding Device Source Fields and Field Updates
|
||||
|
||||
When the system scans a network, it finds various details about devices (like names, IP addresses, and manufacturers). To ensure the data remains accurate without accidentally overwriting manual changes, the system uses a set of "Source Rules."
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## The "Protection" Levels
|
||||
|
||||
Every piece of information for a device has a **Source**. This source determines whether a new scan is allowed to change that value.
|
||||
|
||||
| Source Status | Description | Can a Scan Overwrite it? |
|
||||
| --- | --- | --- |
|
||||
| **USER** | You manually entered this value. | **Never** |
|
||||
| **LOCKED** | This value is pinned and protected. | **Never** |
|
||||
| **NEWDEV** | This value was initialized from `NEWDEV` plugin settings. | **Always** |
|
||||
| **(Plugin Name)** | The value was found by a specific scanner (e.g., `NBTSCAN`). | **Only if specific rules are met** |
|
||||
|
||||
---
|
||||
|
||||
## How Scans Update Information
|
||||
|
||||
If a field is **not** protected by a `USER` or `LOCKED` status, the system follows these rules to decide if it should update the info:
|
||||
|
||||
### 1. The "Empty Field" Rule (Default)
|
||||
|
||||
By default, the system is cautious. It will only fill in a piece of information if the current field is **empty** (showing as "unknown," "0.0.0.0," or blank). It won't change for example an existing name unless you tell it to.
|
||||
|
||||
### 2. SET_ALWAYS
|
||||
|
||||
Some plugins are configured to be "authoritative." If a field is in the **SET_ALWAYS** setting of a plugin:
|
||||
|
||||
* The scanner will **always** overwrite the current value with the new one.
|
||||
* *Note: It will still never overwrite a `USER` or `LOCKED` field.*
|
||||
|
||||
### 3. SET_EMPTY
|
||||
|
||||
If a field is in the **SET_EMPTY** list:
|
||||
|
||||
* The scanner will **only** provide a value if the current field is currently empty.
|
||||
* This is used for fields where we want to "fill in the blanks" but never change a value once it has been established by any source.
|
||||
|
||||
### 4. Automatic Overrides (Live Tracking)
|
||||
|
||||
Some fields, like **IP Addresses** (`devLastIP`) and **Full Domain Names** (`devFQDN`), are set to automatically update whenever they change. This ensures that if a device moves to a new IP on your network, the system reflects that change immediately without you having to do anything.
|
||||
|
||||
---
|
||||
|
||||
## Summary of Field Logic
|
||||
|
||||
| If the current value is... | And the Scan finds... | Does it update? |
|
||||
| --- | --- | --- |
|
||||
| **USER / LOCKED** | Anything | **No** |
|
||||
| **Empty** | A new value | **Yes** |
|
||||
| **A "Plugin" value** | A different value | **No** (Unless `SET_ALWAYS` is on) |
|
||||
| **An IP Address** | A different IP | **Yes** (Updates automatically) |
|
||||
|
||||
## See also:
|
||||
|
||||
- [Device locking](./DEVICE_FIELD_LOCK.md)
|
||||
- [Device source fields](./DEVICE_SOURCE_FIELDS.md)
|
||||
- [API Device Endpoints Documentation](./API_DEVICE.md)
|
||||
- [Authoritative Field Updates System](./PLUGINS_DEV.md#authoritative-fields)
|
||||
- [Plugin Configuration Reference](./PLUGINS_DEV_CONFIG.md)
|
||||
- [Device locking APIs](API_DEVICE_FIELD_LOCK.md)
|
||||
- [Device management](DEVICE_MANAGEMENT.md)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -339,7 +339,7 @@ function getDeviceData() {
|
||||
</i>
|
||||
</label>
|
||||
<div class="${obj.inputClasses}">
|
||||
${generateFormHtml(settingsData, setting, fieldData.toString(), fieldOptionsOverride, null)}
|
||||
${generateFormHtml(settingsData, setting, fieldData.toString(), fieldOptionsOverride, null, mac == "new")}
|
||||
${inlineControl}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -543,7 +543,10 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
|
||||
"devCustomProps", // 26
|
||||
"devFQDN", // 27
|
||||
"devParentRelType", // 28
|
||||
"devReqNicsOnline" // 29
|
||||
"devReqNicsOnline", // 29
|
||||
"devVlan", // 30
|
||||
"devPrimaryIPv4", // 31
|
||||
"devPrimaryIPv6", // 32
|
||||
];
|
||||
|
||||
// console.log("OrderBy: " + columnNames[tableColumnOrder[index]]);
|
||||
@@ -660,6 +663,9 @@ function initializeDatatable (status) {
|
||||
devFQDN
|
||||
devParentRelType
|
||||
devReqNicsOnline
|
||||
devVlan
|
||||
devPrimaryIPv4
|
||||
devPrimaryIPv6
|
||||
}
|
||||
count
|
||||
}
|
||||
@@ -743,7 +749,10 @@ function initializeDatatable (status) {
|
||||
device.devCustomProps || "",
|
||||
device.devFQDN || "",
|
||||
device.devParentRelType || "",
|
||||
device.devReqNicsOnline || 0
|
||||
device.devReqNicsOnline || 0,
|
||||
device.devVlan || "",
|
||||
device.devPrimaryIPv4 || "",
|
||||
device.devPrimaryIPv6 || "",
|
||||
];
|
||||
|
||||
const newRow = [];
|
||||
|
||||
453
front/img/svg/netalertx_blue_docs.svg
Normal file
453
front/img/svg/netalertx_blue_docs.svg
Normal file
@@ -0,0 +1,453 @@
|
||||
<?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_3.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="3373"
|
||||
inkscape:window-height="1417"
|
||||
inkscape:window-x="59"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
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" />
|
||||
<ellipse
|
||||
style="display:inline;fill:#ffffff;stroke-width:0.176318"
|
||||
id="path31-89"
|
||||
mask="url(#mask49405)"
|
||||
transform="translate(-99.990036,0.02979629)"
|
||||
cy="26.458334"
|
||||
cx="126.45834"
|
||||
rx="26.458"
|
||||
ry="26.458334" />
|
||||
<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:#4051b5;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:#4051b5;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:#4051b5;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 |
@@ -181,7 +181,7 @@ function getSettingOptions (key) {
|
||||
|
||||
if (result == "")
|
||||
{
|
||||
console.log(`Setting options with key "${key}" not found`)
|
||||
// console.log(`Setting options with key "${key}" not found`)
|
||||
result = []
|
||||
}
|
||||
|
||||
@@ -197,10 +197,10 @@ function getSetting (key) {
|
||||
|
||||
result = getCache(`nax_set_${key}`, true);
|
||||
|
||||
if (result == "")
|
||||
{
|
||||
console.log(`Setting with key "${key}" not found`)
|
||||
}
|
||||
// if (result == "")
|
||||
// {
|
||||
// console.log(`Setting with key "${key}" not found`)
|
||||
// }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,8 @@ function showModalPopupForm(
|
||||
curValue = null,
|
||||
popupFormJson = null,
|
||||
parentSettingKey = null,
|
||||
triggeredBy = null
|
||||
triggeredBy = null,
|
||||
populateFromOverrides = true
|
||||
) {
|
||||
// set captions
|
||||
prefix = "modal-form";
|
||||
@@ -229,7 +230,8 @@ function showModalPopupForm(
|
||||
setObj,
|
||||
null,
|
||||
fieldOptionsOverride,
|
||||
null
|
||||
null,
|
||||
populateFromOverrides // is new entry
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -321,7 +321,8 @@ function addViaPopupForm(element) {
|
||||
null, // curValue
|
||||
popupFormJson, // popupform
|
||||
toId, // parentSettingKey
|
||||
element // triggeredBy
|
||||
element, // triggeredBy
|
||||
true // initialize defaut values
|
||||
);
|
||||
|
||||
// flag something changes to prevent navigating from page
|
||||
@@ -475,7 +476,8 @@ function initListInteractionOptions(element) {
|
||||
curValue, // curValue
|
||||
popupFormJson, // popupform
|
||||
toId, // parentSettingKey
|
||||
this // triggeredBy
|
||||
this, // triggeredBy
|
||||
true // populate overrides
|
||||
);
|
||||
} else {
|
||||
// Fallback to normal field input
|
||||
@@ -1132,24 +1134,44 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Generate the form control for setting
|
||||
function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) {
|
||||
/**
|
||||
* Generates the HTML string for form controls based on setting configurations.
|
||||
* Supports various element types including select, input, button, textarea, span, and recursive datatables.
|
||||
* * @param {Object} settingsData - The global settings object containing configuration for all available settings.
|
||||
* @param {Object} set - The specific configuration object for the current setting.
|
||||
* @param {string} set.setKey - Unique identifier for the setting.
|
||||
* @param {string} set.setType - JSON string defining the UI components (dataType, elements, etc.).
|
||||
* @param {string} [set.setValue] - The default value for the setting.
|
||||
* @param {Array|string} [set.setEvents] - List of event triggers to be rendered as clickable icons.
|
||||
* @param {any} overrideValue - The current value to be displayed in the form control.
|
||||
* @param {any} overrideOptions - Custom options to override the default setting options (used primarily for dropdowns).
|
||||
* @param {string} originalSetKey - The base key name (used to maintain reference when keys are modified for uniqueness in tables).
|
||||
* @param {boolean} populateFromOverrides - Flag to determine if the value should be pulled from `set['setValue']` (true) or `overrideValue` (false).
|
||||
* * @returns {string} A string of HTML containing the form elements and any associated event action icons.
|
||||
* * @example
|
||||
* const html = generateFormHtml(allSettings, currentSet, "DefaultVal", null, "my_key", false);
|
||||
* $('#container').html(html);
|
||||
*/
|
||||
function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey, populateFromOverrides) {
|
||||
let inputHtml = '';
|
||||
|
||||
|
||||
// if override value is considered empty initialize from setting defaults
|
||||
overrideValue == null || overrideValue == undefined ? inVal = set['setValue'] : inVal = overrideValue
|
||||
populateFromOverrides ? inVal = set['setValue'] : inVal = overrideValue;
|
||||
|
||||
const setKey = set['setKey'];
|
||||
const setType = set['setType'];
|
||||
|
||||
// if (setKey == 'NEWDEV_devParentMAC') {
|
||||
// if (setKey == 'UNIFIAPI_site_name') {
|
||||
|
||||
// console.log("==== DEBUG OUTPUT BELOW 1 ====");
|
||||
// console.log(setType);
|
||||
// console.log(setKey);
|
||||
// console.log(overrideValue);
|
||||
// console.log(inVal);
|
||||
|
||||
// }
|
||||
// console.log("==== DEBUG OUTPUT BELOW 1 ====");
|
||||
// console.log("populateFromOverrides: " + populateFromOverrides);
|
||||
// console.log(setType);
|
||||
// console.log(setKey);
|
||||
// console.log("overrideValue:" + overrideValue);
|
||||
// console.log("inVal:" + inVal);
|
||||
// console.log("set['setValue']:" + set['setValue']);
|
||||
// }
|
||||
|
||||
// Parse the setType JSON string
|
||||
// console.log(processQuotes(setType));
|
||||
@@ -1189,15 +1211,14 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
||||
// Override value
|
||||
let val = valRes;
|
||||
|
||||
// if (setKey == 'NEWDEV_devParentMAC') {
|
||||
// if (setKey == 'UNIFIAPI_site_name') {
|
||||
|
||||
// console.log("==== DEBUG OUTPUT BELOW 2 ====");
|
||||
// console.log(setType);
|
||||
// console.log(setKey);
|
||||
// console.log(overrideValue);
|
||||
// console.log(inVal);
|
||||
// console.log(val);
|
||||
|
||||
// console.log("overrideValue:" + overrideValue);
|
||||
// console.log("inVal:" + inVal);
|
||||
// console.log("val:" + val);
|
||||
// }
|
||||
|
||||
// Generate HTML based on elementType
|
||||
@@ -1227,7 +1248,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
||||
break;
|
||||
|
||||
case 'input':
|
||||
const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : '';
|
||||
const checked = val === 'True' || val === 'true' || val === '1' || val == true ? 'checked' : '';
|
||||
const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control';
|
||||
|
||||
inputHtml += `<input
|
||||
@@ -1347,22 +1368,23 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
|
||||
// Extract the value for the current column
|
||||
let columnOverrideValue = rowData[j] && Object.values(rowData[j])[0];
|
||||
|
||||
if(columnOverrideValue == undefined)
|
||||
{
|
||||
columnOverrideValue = ""
|
||||
}
|
||||
|
||||
// Create unique key to prevent dropdown data duplication
|
||||
const oldKey = set["setKey"];
|
||||
set["setKey"] = oldKey + "_" + index;
|
||||
|
||||
// console.log("settingsData");
|
||||
// console.log(settingsData);
|
||||
// console.log(set);
|
||||
|
||||
|
||||
// Generate the cell HTML using the extracted value
|
||||
const cellHtml = generateFormHtml(
|
||||
settingsData,
|
||||
set,
|
||||
columnOverrideValue.toString(),
|
||||
columnOverrideValue,
|
||||
set["setOptions"],
|
||||
oldKey
|
||||
oldKey,
|
||||
false
|
||||
);
|
||||
datatableHtml += `<td> <div class="input-group"> ${cellHtml} </div></td>`;
|
||||
|
||||
|
||||
@@ -145,14 +145,18 @@ class NetAlertXStateManager {
|
||||
.attr('data-version', version);
|
||||
|
||||
// 3. Update Build Timestamp placeholders
|
||||
const buildTime = appState["buildTimestamp"] !== undefined ? appState["buildTimestamp"] : "";
|
||||
const displayTime = (buildTime === 0) ? "UNKNOWN" : buildTime;
|
||||
const buildTime = appState["buildTimestamp"] || 0;
|
||||
const displayTime = buildTime ? localizeTimestamp(buildTime) : "UNKNOWN";
|
||||
|
||||
$('[data-plc="build-timestamp"]')
|
||||
.html(displayTime)
|
||||
.attr('data-build-time', buildTime);
|
||||
|
||||
$('[data-plc="build-timestamp"]')
|
||||
.html(displayTime)
|
||||
.attr('data-build-time', buildTime);
|
||||
|
||||
console.log("[NetAlertX State] UI updated via jQuery");
|
||||
// console.log("[NetAlertX State] UI updated via jQuery");
|
||||
} catch (e) {
|
||||
console.error("[NetAlertX State] Failed to update state display:", e);
|
||||
}
|
||||
|
||||
@@ -668,7 +668,10 @@ function getColumnNameFromLangString(headStringKey) {
|
||||
"Device_TableHead_CustomProps": "devCustomProps",
|
||||
"Device_TableHead_FQDN": "devFQDN",
|
||||
"Device_TableHead_ParentRelType": "devParentRelType",
|
||||
"Device_TableHead_ReqNicsOnline": "devReqNicsOnline"
|
||||
"Device_TableHead_ReqNicsOnline": "devReqNicsOnline",
|
||||
"Device_TableHead_Vlan": "devVlan",
|
||||
"Device_TableHead_IPv4": "devPrimaryIPv4",
|
||||
"Device_TableHead_IPv6": "devPrimaryIPv6"
|
||||
};
|
||||
|
||||
return columnNameMap[headStringKey] || "";
|
||||
|
||||
@@ -18,7 +18,7 @@ function renderFilterDropdown($headerKey, $columnName, $values) {
|
||||
// Generate the dropdown HTML
|
||||
return '
|
||||
<div class="filter-group input-group">
|
||||
<label for="filter_' . htmlspecialchars($columnName) . '">' . lang($headerKey) . '</label>
|
||||
<label for="filter_' . htmlspecialchars($columnName) . '">' . lang($headerKey) . ': </label>
|
||||
<select id="filter_' . htmlspecialchars($columnName) . '" class="filter-dropdown" data-column="' . htmlspecialchars($columnName) . '">
|
||||
' . $optionsHtml . '
|
||||
</select>
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "أول جلسة",
|
||||
"Device_TableHead_GUID": "معرف فريد",
|
||||
"Device_TableHead_Group": "المجموعة",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "الأيقونة",
|
||||
"Device_TableHead_LastIP": "آخر عنوان IP",
|
||||
"Device_TableHead_LastIPOrder": "ترتيب آخر عنوان IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "اسم عقدة المزامنة",
|
||||
"Device_TableHead_Type": "النوع",
|
||||
"Device_TableHead_Vendor": "المصنع",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "ليس جهاز شبكة",
|
||||
"Device_Table_info": "معلومات الجدول",
|
||||
"Device_Table_nav_next": "التالي",
|
||||
@@ -783,4 +786,4 @@
|
||||
"settings_system_label": "نظام",
|
||||
"settings_update_item_warning": "قم بتحديث القيمة أدناه. احرص على اتباع التنسيق السابق. <b>لم يتم إجراء التحقق.</b>",
|
||||
"test_event_tooltip": "احفظ التغييرات أولاً قبل اختبار الإعدادات."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Primera Sessió",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grup",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Icona",
|
||||
"Device_TableHead_LastIP": "Darrera IP",
|
||||
"Device_TableHead_LastIPOrder": "Últim Ordre d'IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Node Sync",
|
||||
"Device_TableHead_Type": "Tipus",
|
||||
"Device_TableHead_Vendor": "Venedor",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "No configurat com a dispositiu de xarxa",
|
||||
"Device_Table_info": "Mostrant _INICI_ a_FINAL_ d'entrades_ TOTALS",
|
||||
"Device_Table_nav_next": "Següent",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "",
|
||||
"Device_TableHead_GUID": "",
|
||||
"Device_TableHead_Group": "",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "",
|
||||
"Device_TableHead_LastIP": "",
|
||||
"Device_TableHead_LastIPOrder": "",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "",
|
||||
"Device_TableHead_Type": "",
|
||||
"Device_TableHead_Vendor": "",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "",
|
||||
"Device_Table_info": "",
|
||||
"Device_Table_nav_next": "",
|
||||
|
||||
@@ -230,6 +230,8 @@
|
||||
"Device_TableHead_FirstSession": "Erste Sitzung",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Gruppe",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Icon",
|
||||
"Device_TableHead_LastIP": "Letzte IP",
|
||||
"Device_TableHead_LastIPOrder": "Letzte erhaltene IP",
|
||||
@@ -253,6 +255,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Synchronisationsknoten",
|
||||
"Device_TableHead_Type": "Typ",
|
||||
"Device_TableHead_Vendor": "Hersteller",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Nicht konfiguriert als Netzwerkgerät",
|
||||
"Device_Table_info": "Zeige _START_ bis _END_ von _TOTAL_ Einträgen",
|
||||
"Device_Table_nav_next": "Nächste",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "First Session",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Group",
|
||||
"Device_TableHead_IPv4": "IPv4",
|
||||
"Device_TableHead_IPv6": "IPv6",
|
||||
"Device_TableHead_Icon": "Icon",
|
||||
"Device_TableHead_LastIP": "Last IP",
|
||||
"Device_TableHead_LastIPOrder": "Last IP Order",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Sync Node",
|
||||
"Device_TableHead_Type": "Type",
|
||||
"Device_TableHead_Vendor": "Vendor",
|
||||
"Device_TableHead_Vlan": "VLAN",
|
||||
"Device_Table_Not_Network_Device": "Not configured as a network device",
|
||||
"Device_Table_info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||
"Device_Table_nav_next": "Next",
|
||||
|
||||
@@ -228,6 +228,8 @@
|
||||
"Device_TableHead_FirstSession": "1ra. sesión",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grupo",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Icon",
|
||||
"Device_TableHead_LastIP": "Última IP",
|
||||
"Device_TableHead_LastIPOrder": "Última orden de IP",
|
||||
@@ -251,6 +253,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Nodo de sincronización",
|
||||
"Device_TableHead_Type": "Tipo",
|
||||
"Device_TableHead_Vendor": "Fabricante",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "No está configurado como dispositivo de red",
|
||||
"Device_Table_info": "Mostrando el INICIO y el FINAL de TODAS las entradas",
|
||||
"Device_Table_nav_next": "Siguiente",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "",
|
||||
"Device_TableHead_GUID": "",
|
||||
"Device_TableHead_Group": "",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "",
|
||||
"Device_TableHead_LastIP": "",
|
||||
"Device_TableHead_LastIPOrder": "",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "",
|
||||
"Device_TableHead_Type": "",
|
||||
"Device_TableHead_Vendor": "",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "",
|
||||
"Device_Table_info": "",
|
||||
"Device_Table_nav_next": "",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Première session",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Groupe",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Icône",
|
||||
"Device_TableHead_LastIP": "Dernière IP",
|
||||
"Device_TableHead_LastIPOrder": "Ordre dernière IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Noeud de synchro",
|
||||
"Device_TableHead_Type": "Type",
|
||||
"Device_TableHead_Vendor": "Fabriquant",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Non configuré comme appareil du réseau",
|
||||
"Device_Table_info": "Affiche de _START_ à _END_ sur _TOTAL_ entrées",
|
||||
"Device_Table_nav_next": "Suivant",
|
||||
@@ -783,4 +786,4 @@
|
||||
"settings_system_label": "Système",
|
||||
"settings_update_item_warning": "Mettre à jour la valeur ci-dessous. Veillez à bien suivre le même format qu'auparavant. <b>Il n'y a pas de pas de contrôle.</b>",
|
||||
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
|
||||
}
|
||||
}
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Prima sessione",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Gruppo",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Icona",
|
||||
"Device_TableHead_LastIP": "Ultimo IP",
|
||||
"Device_TableHead_LastIPOrder": "Ordina per ultimo IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Sincronizza nodo",
|
||||
"Device_TableHead_Type": "Tipo",
|
||||
"Device_TableHead_Vendor": "Produttore",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Non configurato come dispositivo di rete",
|
||||
"Device_Table_info": "Visualizzazione da _START_ a _END_ di _TOTAL_ voci",
|
||||
"Device_Table_nav_next": "Successivo",
|
||||
@@ -783,4 +786,4 @@
|
||||
"settings_system_label": "Sistema",
|
||||
"settings_update_item_warning": "Aggiorna il valore qui sotto. Fai attenzione a seguire il formato precedente. <b>La convalida non viene eseguita.</b>",
|
||||
"test_event_tooltip": "Salva le modifiche prima di provare le nuove impostazioni."
|
||||
}
|
||||
}
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "初回セッション",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "グループ",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "アイコン",
|
||||
"Device_TableHead_LastIP": "直近のIP",
|
||||
"Device_TableHead_LastIPOrder": "直近のIP順",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "同期ノード",
|
||||
"Device_TableHead_Type": "種別",
|
||||
"Device_TableHead_Vendor": "ベンダー",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "ネットワーク機器として構成されていない",
|
||||
"Device_Table_info": "_START_~_END_を表示 / _TOTAL_ 件中",
|
||||
"Device_Table_nav_next": "次",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Første Økt",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Gruppe",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Ikon",
|
||||
"Device_TableHead_LastIP": "Siste IP",
|
||||
"Device_TableHead_LastIPOrder": "Siste IP Rekkefølge",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Synkroniser Node",
|
||||
"Device_TableHead_Type": "Type",
|
||||
"Device_TableHead_Vendor": "Leverandør",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Ikke konfigurert som en nettverksenhet",
|
||||
"Device_Table_info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||
"Device_Table_nav_next": "Neste",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Pierwsza sesja",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grupa",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Ikona",
|
||||
"Device_TableHead_LastIP": "Ostatni adres IP",
|
||||
"Device_TableHead_LastIPOrder": "Kolejność ostatniego adresu IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Węzeł synchronizacji",
|
||||
"Device_TableHead_Type": "Typ",
|
||||
"Device_TableHead_Vendor": "Producent",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Nie skonfigurowano jako urządzenie sieciowe",
|
||||
"Device_Table_info": "Pokazuje _START_ do _END_ z _TOTAL_ wpisów",
|
||||
"Device_Table_nav_next": "Następna",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Primeira sessão",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grupo",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Ícone",
|
||||
"Device_TableHead_LastIP": "Último IP",
|
||||
"Device_TableHead_LastIPOrder": "Último pedido de IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Nó de sincronização",
|
||||
"Device_TableHead_Type": "Tipo",
|
||||
"Device_TableHead_Vendor": "Fornecedor",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Não configurado como um dispositivo de rede",
|
||||
"Device_Table_info": "Mostrando _START_ de _END_ do _TOTAL_ entradas",
|
||||
"Device_Table_nav_next": "Próximo",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Primeira sessão",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grupo",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Ícone",
|
||||
"Device_TableHead_LastIP": "Último IP",
|
||||
"Device_TableHead_LastIPOrder": "Último pedido de IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Nó de sincronização",
|
||||
"Device_TableHead_Type": "Tipo",
|
||||
"Device_TableHead_Vendor": "Fornecedor",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Não configurado como um dispositivo de rede",
|
||||
"Device_Table_info": "A mostrar _START_ to _END_ of _TOTAL_ entradas",
|
||||
"Device_Table_nav_next": "Próximo",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Первый сеанс",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Группа",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Значок",
|
||||
"Device_TableHead_LastIP": "Последний IP",
|
||||
"Device_TableHead_LastIPOrder": "Последний IP-запрос",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Узел синхронизации",
|
||||
"Device_TableHead_Type": "Тип",
|
||||
"Device_TableHead_Vendor": "Поставщик",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Не настроено как сетевое устройство",
|
||||
"Device_Table_info": "Показаны с _START_ по _END_ из _TOTAL_ записей",
|
||||
"Device_Table_nav_next": "Следующая",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "",
|
||||
"Device_TableHead_GUID": "",
|
||||
"Device_TableHead_Group": "",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "",
|
||||
"Device_TableHead_LastIP": "",
|
||||
"Device_TableHead_LastIPOrder": "",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "",
|
||||
"Device_TableHead_Type": "",
|
||||
"Device_TableHead_Vendor": "",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "",
|
||||
"Device_Table_info": "",
|
||||
"Device_Table_nav_next": "",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "İlk Oturum",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Grup",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "İkon",
|
||||
"Device_TableHead_LastIP": "Son IP",
|
||||
"Device_TableHead_LastIPOrder": "Son IP Sırası",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Senkronizasyon Node",
|
||||
"Device_TableHead_Type": "Tür",
|
||||
"Device_TableHead_Vendor": "Üretici",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Ağ cihazı olarak ayarlanmadı",
|
||||
"Device_Table_info": "Showing _START_ to _END_ of _TOTAL_ entries",
|
||||
"Device_Table_nav_next": "Sonraki",
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "Перша сесія",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "Група",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "Значок",
|
||||
"Device_TableHead_LastIP": "Останній IP",
|
||||
"Device_TableHead_LastIPOrder": "Останнє замовлення IP",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "Вузол синхронізації",
|
||||
"Device_TableHead_Type": "Тип",
|
||||
"Device_TableHead_Vendor": "Продавець",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "Не налаштовано як мережевий пристрій",
|
||||
"Device_Table_info": "Показано від _START_ до _END_ із _TOTAL_ записів",
|
||||
"Device_Table_nav_next": "Далі",
|
||||
@@ -783,4 +786,4 @@
|
||||
"settings_system_label": "Система",
|
||||
"settings_update_item_warning": "Оновіть значення нижче. Слідкуйте за попереднім форматом. <b>Перевірка не виконана.</b>",
|
||||
"test_event_tooltip": "Перш ніж перевіряти налаштування, збережіть зміни."
|
||||
}
|
||||
}
|
||||
@@ -226,6 +226,8 @@
|
||||
"Device_TableHead_FirstSession": "加入",
|
||||
"Device_TableHead_GUID": "GUID",
|
||||
"Device_TableHead_Group": "组",
|
||||
"Device_TableHead_IPv4": "",
|
||||
"Device_TableHead_IPv6": "",
|
||||
"Device_TableHead_Icon": "图标",
|
||||
"Device_TableHead_LastIP": "上次 IP",
|
||||
"Device_TableHead_LastIPOrder": "上次 IP 排序",
|
||||
@@ -249,6 +251,7 @@
|
||||
"Device_TableHead_SyncHubNodeName": "同步节点",
|
||||
"Device_TableHead_Type": "类型",
|
||||
"Device_TableHead_Vendor": "制造商",
|
||||
"Device_TableHead_Vlan": "",
|
||||
"Device_Table_Not_Network_Device": "未配置为网络设备",
|
||||
"Device_Table_info": "显示第_START_至 END_条_共_TOTAL_条",
|
||||
"Device_Table_nav_next": "下一页",
|
||||
|
||||
@@ -1588,6 +1588,38 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "devVlan",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxLength": 50,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "VLAN"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "The VLAN identifier or name the device belongs to. Database column name: <code>devVlan</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "devSyncHubNode",
|
||||
"type": {
|
||||
@@ -1901,38 +1933,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "devVlan",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxLength": 50,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"localized": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "VLAN"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "The VLAN identifier or name the device belongs to. Database column name: <code>devVlan</code>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "devForceStatus",
|
||||
"type": {
|
||||
|
||||
@@ -440,7 +440,10 @@
|
||||
"Device_TableHead_CustomProps",
|
||||
"Device_TableHead_FQDN",
|
||||
"Device_TableHead_ParentRelType",
|
||||
"Device_TableHead_ReqNicsOnline"
|
||||
"Device_TableHead_ReqNicsOnline",
|
||||
"Device_TableHead_Vlan",
|
||||
"Device_TableHead_IPv4",
|
||||
"Device_TableHead_IPv6"
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
@@ -517,7 +520,8 @@
|
||||
"Device_TableHead_NetworkSite",
|
||||
"Device_TableHead_SSID",
|
||||
"Device_TableHead_SourcePlugin",
|
||||
"Device_TableHead_ParentRelType"
|
||||
"Device_TableHead_ParentRelType",
|
||||
"Device_TableHead_Vlan"
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
|
||||
@@ -511,10 +511,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
}
|
||||
|
||||
// INPUT
|
||||
inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null);
|
||||
inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null, false);
|
||||
|
||||
// construct final HTML for the setting
|
||||
setHtml += inputFormHtml + overrideHtml + `
|
||||
// construct final HTML for the setting
|
||||
setHtml += inputFormHtml + overrideHtml + `
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -6,17 +6,46 @@ if [ -d "${NETALERTX_CONFIG}" ]; then
|
||||
chmod u+rwX "${NETALERTX_CONFIG}" 2>/dev/null || true
|
||||
fi
|
||||
chmod u+rw "${NETALERTX_CONFIG}/app.conf" 2>/dev/null || true
|
||||
|
||||
set -eu
|
||||
|
||||
CYAN=$(printf '\033[1;36m')
|
||||
RED=$(printf '\033[1;31m')
|
||||
RESET=$(printf '\033[0m')
|
||||
|
||||
# Ensure config folder exists
|
||||
if [ ! -d "${NETALERTX_CONFIG}" ]; then
|
||||
if ! mkdir -p "${NETALERTX_CONFIG}"; then
|
||||
>&2 printf "%s" "${RED}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
❌ Error creating config folder in: ${NETALERTX_CONFIG}
|
||||
|
||||
A config directory is required for proper operation, however there appear to be
|
||||
insufficient permissions on this mount or it is otherwise inaccessible.
|
||||
|
||||
More info: https://docs.netalertx.com/FILE_PERMISSIONS
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
>&2 printf "%s" "${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
chmod 700 "${NETALERTX_CONFIG}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Fresh rebuild requested
|
||||
if [ "${ALWAYS_FRESH_INSTALL:-false}" = "true" ] && [ -e "${NETALERTX_CONFIG}/app.conf" ]; then
|
||||
>&2 echo "INFO: ALWAYS_FRESH_INSTALL enabled — removing existing config."
|
||||
rm -rf "${NETALERTX_CONFIG}"/*
|
||||
fi
|
||||
|
||||
# Check for app.conf and deploy if required
|
||||
if [ ! -f "${NETALERTX_CONFIG}/app.conf" ]; then
|
||||
mkdir -p "${NETALERTX_CONFIG}" || {
|
||||
>&2 echo "ERROR: Failed to create config directory ${NETALERTX_CONFIG}"
|
||||
exit 1
|
||||
}
|
||||
install -m 600 /app/back/app.conf "${NETALERTX_CONFIG}/app.conf" || {
|
||||
>&2 echo "ERROR: Failed to deploy default config to ${NETALERTX_CONFIG}/app.conf"
|
||||
exit 2
|
||||
}
|
||||
RESET=$(printf '\033[0m')
|
||||
>&2 printf "%s" "${CYAN}"
|
||||
>&2 cat <<EOF
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
🆕 First run detected. Default configuration written to ${NETALERTX_CONFIG}/app.conf.
|
||||
@@ -25,7 +54,6 @@ if [ ! -f "${NETALERTX_CONFIG}/app.conf" ]; then
|
||||
this instance in production.
|
||||
══════════════════════════════════════════════════════════════════════════════
|
||||
EOF
|
||||
|
||||
>&2 printf "%s" "${RESET}"
|
||||
>&2 printf "%s" "${RESET}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -339,6 +339,9 @@ class Query(ObjectType):
|
||||
"devFQDN",
|
||||
"devParentRelType",
|
||||
"devParentMAC",
|
||||
"devVlan",
|
||||
"devPrimaryIPv4",
|
||||
"devPrimaryIPv6"
|
||||
]
|
||||
|
||||
search_term = options.search.lower()
|
||||
|
||||
@@ -176,6 +176,12 @@ sql_devices_filters = """
|
||||
SELECT DISTINCT 'devSyncHubNode' AS columnName, devSyncHubNode AS columnValue
|
||||
FROM Devices WHERE devSyncHubNode NOT IN ('', 'null') AND devSyncHubNode IS NOT NULL
|
||||
UNION
|
||||
SELECT DISTINCT 'devVlan' AS columnName, devVlan AS columnValue
|
||||
FROM Devices WHERE devVlan NOT IN ('', 'null') AND devVlan IS NOT NULL
|
||||
UNION
|
||||
SELECT DISTINCT 'devParentRelType' AS columnName, devParentRelType AS columnValue
|
||||
FROM Devices WHERE devParentRelType NOT IN ('', 'null') AND devParentRelType IS NOT NULL
|
||||
UNION
|
||||
SELECT DISTINCT 'devSSID' AS columnName, devSSID AS columnValue
|
||||
FROM Devices WHERE devSSID NOT IN ('', 'null') AND devSSID IS NOT NULL
|
||||
ORDER BY columnName;
|
||||
|
||||
@@ -448,4 +448,3 @@ def unlock_fields(conn, mac=None, fields=None, clear_all=False):
|
||||
}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@@ -590,26 +590,6 @@ def normalize_string(text):
|
||||
# MAC and IP helper methods
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# # -------------------------------------------------------------------------------------------
|
||||
# def is_random_mac(mac: str) -> bool:
|
||||
# """Determine if a MAC address is random, respecting user-defined prefixes not to mark as random."""
|
||||
|
||||
# is_random = mac[1].upper() in ["2", "6", "A", "E"]
|
||||
|
||||
# # Get prefixes from settings
|
||||
# prefixes = get_setting_value("UI_NOT_RANDOM_MAC")
|
||||
|
||||
# # If detected as random, make sure it doesn't start with a prefix the user wants to exclude
|
||||
# if is_random:
|
||||
# for prefix in prefixes:
|
||||
# if mac.upper().startswith(prefix.upper()):
|
||||
# is_random = False
|
||||
# break
|
||||
|
||||
# return is_random
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------
|
||||
def generate_mac_links(html, deviceUrl):
|
||||
p = re.compile(r"(?:[0-9a-fA-F]:?){12}")
|
||||
|
||||
@@ -3,6 +3,7 @@ import sys
|
||||
import json
|
||||
import uuid
|
||||
import time
|
||||
import fcntl
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@@ -19,6 +20,35 @@ from api_server.sse_broadcast import broadcast_unread_notifications_count # noq
|
||||
NOTIFICATION_API_FILE = apiPath + 'user_notifications.json'
|
||||
|
||||
|
||||
def locked_notifications_file(callback):
|
||||
# Ensure file exists
|
||||
if not os.path.exists(NOTIFICATION_API_FILE):
|
||||
with open(NOTIFICATION_API_FILE, "w") as f:
|
||||
f.write("[]")
|
||||
|
||||
with open(NOTIFICATION_API_FILE, "r+") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
try:
|
||||
raw = f.read().strip() or "[]"
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
mylog("none", "[Notification] Corrupted JSON detected, resetting.")
|
||||
data = []
|
||||
|
||||
# Let caller modify data
|
||||
result = callback(data)
|
||||
|
||||
# Write back atomically
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
return result
|
||||
finally:
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
|
||||
# Show Frontend User Notification
|
||||
def write_notification(content, level="alert", timestamp=None):
|
||||
"""
|
||||
@@ -36,50 +66,21 @@ def write_notification(content, level="alert", timestamp=None):
|
||||
if timestamp is None:
|
||||
timestamp = timeNowDB()
|
||||
|
||||
# Generate GUID
|
||||
guid = str(uuid.uuid4())
|
||||
|
||||
# Prepare notification dictionary
|
||||
notification = {
|
||||
"timestamp": str(timestamp),
|
||||
"guid": guid,
|
||||
"guid": str(uuid.uuid4()),
|
||||
"read": 0,
|
||||
"level": level,
|
||||
"content": content,
|
||||
}
|
||||
|
||||
# If file exists, load existing data, otherwise initialize as empty list
|
||||
try:
|
||||
if os.path.exists(NOTIFICATION_API_FILE):
|
||||
with open(NOTIFICATION_API_FILE, "r") as file:
|
||||
file_contents = file.read().strip()
|
||||
if file_contents:
|
||||
notifications = json.loads(file_contents)
|
||||
if not isinstance(notifications, list):
|
||||
mylog("error", "[Notification] Invalid format: not a list, resetting")
|
||||
notifications = []
|
||||
else:
|
||||
notifications = []
|
||||
else:
|
||||
notifications = []
|
||||
except Exception as e:
|
||||
mylog("error", [f"[Notification] Error reading notifications file: {e}"])
|
||||
notifications = []
|
||||
def update(notifications):
|
||||
notifications.append(notification)
|
||||
|
||||
# Append new notification
|
||||
notifications.append(notification)
|
||||
locked_notifications_file(update)
|
||||
|
||||
# Write updated data back to file
|
||||
try:
|
||||
with open(NOTIFICATION_API_FILE, "w") as file:
|
||||
json.dump(notifications, file, indent=4)
|
||||
except Exception as e:
|
||||
mylog("error", [f"[Notification] Error writing to notifications file: {e}"])
|
||||
# Don't re-raise, just log. This prevents the API from crashing 500.
|
||||
|
||||
# Broadcast unread count update
|
||||
try:
|
||||
unread_count = sum(1 for n in notifications if n.get("read", 0) == 0)
|
||||
unread_count = sum(1 for n in locked_notifications_file(lambda n: n) if n.get("read", 0) == 0)
|
||||
broadcast_unread_notifications_count(unread_count)
|
||||
except Exception as e:
|
||||
mylog("none", [f"[Notification] Failed to broadcast unread count: {e}"])
|
||||
@@ -147,24 +148,42 @@ def mark_all_notifications_read():
|
||||
"error": str (optional)
|
||||
}
|
||||
"""
|
||||
# If notifications file does not exist, nothing to mark
|
||||
if not os.path.exists(NOTIFICATION_API_FILE):
|
||||
return {"success": True}
|
||||
|
||||
try:
|
||||
with open(NOTIFICATION_API_FILE, "r") as f:
|
||||
notifications = json.load(f)
|
||||
except Exception as e:
|
||||
mylog("none", f"[Notification] Failed to read notifications: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
# Open file in read/write mode and acquire exclusive lock
|
||||
with open(NOTIFICATION_API_FILE, "r+") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
|
||||
for n in notifications:
|
||||
n["read"] = 1
|
||||
try:
|
||||
# Read file contents
|
||||
file_contents = f.read().strip()
|
||||
if file_contents == "":
|
||||
notifications = []
|
||||
else:
|
||||
try:
|
||||
notifications = json.loads(file_contents)
|
||||
except json.JSONDecodeError as e:
|
||||
mylog("none", f"[Notification] Corrupted notifications JSON: {e}")
|
||||
notifications = []
|
||||
|
||||
# Mark all notifications as read
|
||||
for n in notifications:
|
||||
n["read"] = 1
|
||||
|
||||
# Rewrite file safely
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(notifications, f, indent=4)
|
||||
|
||||
finally:
|
||||
# Always release file lock
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
try:
|
||||
with open(NOTIFICATION_API_FILE, "w") as f:
|
||||
json.dump(notifications, f, indent=4)
|
||||
except Exception as e:
|
||||
mylog("none", f"[Notification] Failed to write notifications: {e}")
|
||||
mylog("none", f"[Notification] Failed to read/write notifications: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
mylog("debug", "[Notification] All notifications marked as read.")
|
||||
|
||||
@@ -110,6 +110,7 @@ FIELD_SPECS = {
|
||||
"source_col": "devNameSource",
|
||||
"empty_values": ["", "null", "(unknown)", "(name not found)"],
|
||||
"priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"],
|
||||
"allow_override_if_changed": True,
|
||||
},
|
||||
|
||||
# ==========================================================
|
||||
|
||||
@@ -5,6 +5,7 @@ import base64
|
||||
from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
from logger import mylog
|
||||
from helper import is_random_mac
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
@@ -183,17 +184,21 @@ def guess_device_attributes(
|
||||
type_ = None
|
||||
icon = None
|
||||
|
||||
# --- Strict MAC + vendor rule matching from external file ---
|
||||
# 1. Try strict MAC match first
|
||||
type_, icon = match_mac_and_vendor(mac_clean, vendor, default_type, default_icon)
|
||||
|
||||
# 2. If no strict match, try Name match BEFORE checking for random MAC
|
||||
if not type_ or type_ == default_type:
|
||||
type_, icon = match_name(name, default_type, default_icon)
|
||||
|
||||
# 3. Only if it's STILL not found, apply the Random MAC block
|
||||
if type_ == default_type and is_random_mac(mac):
|
||||
return default_icon, default_type
|
||||
|
||||
# --- Loose Vendor-based fallback ---
|
||||
if not type_ or type_ == default_type:
|
||||
type_, icon = match_vendor(vendor, default_type, default_icon)
|
||||
|
||||
# --- Loose Name-based fallback ---
|
||||
if not type_ or type_ == default_type:
|
||||
type_, icon = match_name(name, default_type, default_icon)
|
||||
|
||||
# --- Loose IP-based fallback ---
|
||||
if (not type_ or type_ == default_type) or (not icon or icon == default_icon):
|
||||
type_, icon = match_ip(ip, default_type, default_icon)
|
||||
@@ -261,4 +266,4 @@ def guess_type(
|
||||
|
||||
# Handler for when this is run as a program instead of called as a module.
|
||||
if __name__ == "__main__":
|
||||
mylog("error", "This module is not intended to be run directly.")
|
||||
mylog("none", "This module is not intended to be run directly.")
|
||||
|
||||
Reference in New Issue
Block a user