diff --git a/front/css/app.css b/front/css/app.css index a3183087..1f665841 100755 --- a/front/css/app.css +++ b/front/css/app.css @@ -2249,9 +2249,8 @@ textarea[readonly], .red-hover-border:hover { - border-color: red !important; - border-width: 1px; - border-style: solid; + border-color: #ff0000 !important; + box-shadow: #ff0000; } @@ -2260,12 +2259,6 @@ textarea[readonly], background-color: red !important; } -#multi-edit-form .form-group -{ - height: 45px; - -} - .top-left-logo { height:35px; diff --git a/front/multiEditCore.php b/front/multiEditCore.php index e3f4c5e3..4484ff8a 100755 --- a/front/multiEditCore.php +++ b/front/multiEditCore.php @@ -59,22 +59,22 @@
Source = USER or Source = LOCKED)."
+ }
+ ]
+ },
+ {
+ "function": "SET_EMPTY",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true" }],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": [],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set empty columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV"
+ }
+ ]
}
],
"database_column_definitions": [
diff --git a/front/plugins/dig_scan/config.json b/front/plugins/dig_scan/config.json
index 7a4d2820..8729f9a5 100755
--- a/front/plugins/dig_scan/config.json
+++ b/front/plugins/dig_scan/config.json
@@ -233,6 +233,68 @@
"string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
}
]
+ },
+ {
+ "function": "SET_ALWAYS",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true"}],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": ["devFQDN"],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set always columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)."
+ }
+ ]
+ },
+ {
+ "function": "SET_EMPTY",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true" }],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": [],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set empty columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV"
+ }
+ ]
}
],
"database_column_definitions": [
diff --git a/front/plugins/nbtscan_scan/config.json b/front/plugins/nbtscan_scan/config.json
index b46ae793..48b8a589 100755
--- a/front/plugins/nbtscan_scan/config.json
+++ b/front/plugins/nbtscan_scan/config.json
@@ -233,6 +233,68 @@
"string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
}
]
+ },
+ {
+ "function": "SET_ALWAYS",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true"}],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": ["devFQDN"],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set always columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)."
+ }
+ ]
+ },
+ {
+ "function": "SET_EMPTY",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true" }],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": [],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set empty columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV"
+ }
+ ]
}
],
"database_column_definitions": [
diff --git a/front/plugins/nslookup_scan/config.json b/front/plugins/nslookup_scan/config.json
index 1589e666..6bf444d0 100755
--- a/front/plugins/nslookup_scan/config.json
+++ b/front/plugins/nslookup_scan/config.json
@@ -233,6 +233,68 @@
"string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
}
]
+ },
+ {
+ "function": "SET_ALWAYS",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true"}],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": ["devFQDN"],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set always columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are treated as authoritative and will overwrite existing values, including those set by other plugins, unless the current value was explicitly set by the user (Source = USER or Source = LOCKED)."
+ }
+ ]
+ },
+ {
+ "function": "SET_EMPTY",
+ "type": {
+ "dataType": "array",
+ "elements": [
+ {
+ "elementType": "select",
+ "elementOptions": [{ "multiple": "true", "ordeable": "true" }],
+ "transformers": []
+ }
+ ]
+ },
+ "default_value": [],
+ "options": [
+ "devFQDN",
+ "devName"
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Set empty columns"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "These columns are only overwritten if they are empty (NULL / empty string) or if their Source is set to NEWDEV"
+ }
+ ]
}
],
"database_column_definitions": [
diff --git a/server/database.py b/server/database.py
index d1d5ff08..07678c07 100755
--- a/server/database.py
+++ b/server/database.py
@@ -208,27 +208,6 @@ class DB:
# Init the AppEvent database table
AppEvent_obj(self)
- # # -------------------------------------------------------------------------------
- # def get_table_as_json(self, sqlQuery):
-
- # # mylog('debug',[ '[Database] - get_table_as_json - Query: ', sqlQuery])
- # try:
- # self.sql.execute(sqlQuery)
- # columnNames = list(map(lambda x: x[0], self.sql.description))
- # rows = self.sql.fetchall()
- # except sqlite3.Error as e:
- # mylog('verbose',[ '[Database] - SQL ERROR: ', e])
- # return json_obj({}, []) # return empty object
-
- # result = {"data":[]}
- # for row in rows:
- # tmp = row_to_json(columnNames, row)
- # result["data"].append(tmp)
-
- # # mylog('debug',[ '[Database] - get_table_as_json - returning ', len(rows), " rows with columns: ", columnNames])
- # # mylog('debug',[ '[Database] - get_table_as_json - returning json ', json.dumps(result) ])
- # return json_obj(result, columnNames)
-
def get_table_as_json(self, sqlQuery, parameters=None):
"""
Wrapper to use the central get_table_as_json helper.
diff --git a/server/db/authoritative_handler.py b/server/db/authoritative_handler.py
index 444170ac..d40508c6 100644
--- a/server/db/authoritative_handler.py
+++ b/server/db/authoritative_handler.py
@@ -96,35 +96,37 @@ def can_overwrite_field(field_name, current_value, current_source, plugin_prefix
bool: True if overwrite allowed.
"""
+ empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None)
+
# Rule 1: USER/LOCKED protected
if current_source in ("USER", "LOCKED"):
return False
- # Rule 2: Must provide a non-empty value or same as current
- empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None)
- if not field_value or (isinstance(field_value, str) and not field_value.strip()):
- if current_value == field_value:
- return True # Allow overwrite if value same
+ # Rule 2: Must provide a non-empty value, otherwise reject
+ if (not field_value) or (field_value in empty_values) or ((isinstance(field_value, str) and not field_value.strip())):
return False
- # Rule 3: SET_ALWAYS
+ # Rule 3: Allow overwrite if value same to update Source fields
+ if current_value == field_value:
+ return True
+
+ # Rule 4: SET_ALWAYS
set_always = plugin_settings.get("set_always", [])
if field_name in set_always:
return True
- # Rule 4: SET_EMPTY
+ # Rule 5: SET_EMPTY
set_empty = plugin_settings.get("set_empty", [])
- empty_values = ("0.0.0.0", "", "null", "(unknown)", "(name not found)", None)
if field_name in set_empty:
if current_value in empty_values:
return True
return False
- # Rule 5: Default - overwrite if current value empty
+ # Rule 6: Default - overwrite if current value empty
if current_value in empty_values:
return True
- # Rule 6: Optional override flag to allow overwrite if value changed (devLastIP)
+ # Rule 7: Optional override flag to allow overwrite if value changed (devLastIP)
if allow_override_if_changed and field_value != current_value:
return True
diff --git a/server/db/db_upgrade.py b/server/db/db_upgrade.py
index 994e7f2f..b79ddf88 100755
--- a/server/db/db_upgrade.py
+++ b/server/db/db_upgrade.py
@@ -295,6 +295,7 @@ def ensure_CurrentScan(sql) -> bool:
scanVlan STRING(250),
scanParentMAC STRING(250),
scanParentPort STRING(250),
+ scanFQDN STRING(250),
scanType STRING(250)
);
""")
diff --git a/server/scan/device_handling.py b/server/scan/device_handling.py
index 972ac25f..dcb24476 100755
--- a/server/scan/device_handling.py
+++ b/server/scan/device_handling.py
@@ -102,17 +102,6 @@ FIELD_SPECS = {
"priority": ["NSLOOKUP", "AVAHISCAN", "NBTSCAN", "DIGSCAN", "ARPSCAN", "DHCPLSS", "NEWDEV", "N/A"],
},
- # ==========================================================
- # DEVICE FQDN
- # ==========================================================
- "devFQDN": {
- "scan_col": "scanName",
- "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,
- },
-
# ==========================================================
# IP ADDRESS (last seen)
# ==========================================================
@@ -297,8 +286,6 @@ def update_devices_data_from_scan(db):
current_source = row_dict.get(f"{field}Source") or ""
new_value = row_dict.get(scan_col)
- mylog("debug", f"[Update Devices] - current_value: {current_value} new_value: {new_value} -> {field}")
-
if can_overwrite_field(
field_name=field,
current_value=current_value,
@@ -326,6 +313,7 @@ def update_devices_data_from_scan(db):
WHERE devMac = ?
"""
+ mylog("debug", f"[Update Devices] - ({source_prefix}) current_value: {current_value} new_value: {new_value} -> {field}")
mylog("debug", f"[Update Devices] - ({source_prefix}) {spec['scan_col']} -> {field}")
mylog("debug", f"[Update Devices] sql_tmp: {sql_tmp}, sql_val: {sql_val}")
sql.execute(sql_tmp, sql_val)
@@ -978,34 +966,38 @@ def update_devices_names(pm):
notFound = 0
for device in devices:
- newName = nameNotFound
- newFQDN = ""
+ resolved_successfully = False
# Attempt each resolution strategy in order
for resolve_fn, label in strategies:
resolved = resolve_fn(device["devMac"], device["devLastIP"])
- # Only use name if resolving both name and FQDN
- newName = resolved.cleaned if resolve_both_name_and_fqdn else None
- newFQDN = resolved.raw
+ # Extract values
+ current_raw = resolved.raw if resolved.raw else ""
+ current_cleaned = resolved.cleaned if resolved.cleaned else ""
+
+ # Validation: Ensure we actually got a real FQDN/Name
+ if (current_raw not in [nameNotFound, "", "localhost."] and " communications error to " not in current_raw):
- # If a valid result is found, record it and stop further attempts
- if (
- newFQDN not in [nameNotFound, "", "localhost."] and " communications error to " not in newFQDN
- ):
foundStats[label] += 1
+ resolved_successfully = True
if resolve_both_name_and_fqdn:
- recordsToUpdate.append([newName, label, newFQDN, label, device["devMac"]])
+ # Logic: If cleaned name is missing, fallback to raw,
+ # but if both are missing, this strategy failed.
+ final_name = current_cleaned if current_cleaned else current_raw
+ recordsToUpdate.append([final_name, label, current_raw, label, device["devMac"]])
else:
- recordsToUpdate.append([newFQDN, label, device["devMac"]])
- break
+ recordsToUpdate.append([current_raw, label, device["devMac"]])
- # If no name was resolved, queue device for "(name not found)" update
- if resolve_both_name_and_fqdn and newName == nameNotFound:
+ break # Stop trying other strategies for this device
+
+ # If after all strategies we found nothing
+ if not resolved_successfully:
notFound += 1
- if device["devName"] != nameNotFound:
- recordsNotFound.append([nameNotFound, device["devMac"]])
+ if resolve_both_name_and_fqdn:
+ if device["devName"] != nameNotFound:
+ recordsNotFound.append([nameNotFound, device["devMac"]])
return recordsToUpdate, recordsNotFound, foundStats, notFound
diff --git a/server/scan/session_events.py b/server/scan/session_events.py
index 3f9f1022..2a051ed8 100755
--- a/server/scan/session_events.py
+++ b/server/scan/session_events.py
@@ -103,7 +103,7 @@ def process_scan(db):
# Clear current scan as processed
# 🐛 CurrentScan DEBUG: comment out below when debugging to keep the CurrentScan table after restarts/scan finishes
- db.sql.execute("DELETE FROM CurrentScan")
+ # db.sql.execute("DELETE FROM CurrentScan")
# re-broadcast unread notifiation count to update FE
update_unread_notifications_count()