mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-10 20:22:02 -07:00
BE+FE: refactor timezone UTC #1506
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
@@ -116,7 +116,7 @@ function initializeEventsDatatable (eventsRows) {
|
|||||||
{
|
{
|
||||||
targets: [0],
|
targets: [0],
|
||||||
'createdCell': function (td, cellData, rowData, row, col) {
|
'createdCell': function (td, cellData, rowData, row, col) {
|
||||||
$(td).html(translateHTMLcodes(localizeTimestamp(cellData)));
|
$(td).html(translateHTMLcodes((cellData)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -447,6 +447,7 @@ function localizeTimestamp(input) {
|
|||||||
return formatSafe(input, tz);
|
return formatSafe(input, tz);
|
||||||
|
|
||||||
function formatSafe(str, tz) {
|
function formatSafe(str, tz) {
|
||||||
|
|
||||||
// CHECK: Does the input string have timezone information?
|
// CHECK: Does the input string have timezone information?
|
||||||
// - Ends with Z: "2026-02-11T11:37:02Z"
|
// - Ends with Z: "2026-02-11T11:37:02Z"
|
||||||
// - Has GMT±offset: "Wed Feb 11 2026 12:34:12 GMT+1100 (...)"
|
// - Has GMT±offset: "Wed Feb 11 2026 12:34:12 GMT+1100 (...)"
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import sys
|
import conf
|
||||||
import os
|
from zoneinfo import ZoneInfo
|
||||||
|
import datetime as dt
|
||||||
# Register NetAlertX directories
|
|
||||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
|
||||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
|
||||||
|
|
||||||
from logger import mylog # noqa: E402 [flake8 lint suppression]
|
from logger import mylog # noqa: E402 [flake8 lint suppression]
|
||||||
from messaging.in_app import write_notification # noqa: E402 [flake8 lint suppression]
|
from messaging.in_app import write_notification # noqa: E402 [flake8 lint suppression]
|
||||||
|
|
||||||
@@ -574,64 +570,92 @@ def is_timestamps_in_utc(sql) -> bool:
|
|||||||
|
|
||||||
def migrate_timestamps_to_utc(sql) -> bool:
|
def migrate_timestamps_to_utc(sql) -> bool:
|
||||||
"""
|
"""
|
||||||
Migrate all timestamp columns in the database from local time to UTC.
|
Safely migrate timestamp columns from local time to UTC.
|
||||||
|
|
||||||
This function determines if migration is needed based on the VERSION setting:
|
Migration rules (fail-safe):
|
||||||
- Fresh installs (no VERSION): Skip migration - timestamps already UTC from timeNowUTC()
|
- Default behaviour: RUN migration unless proven safe to skip
|
||||||
- Version >= 26.2.6: Skip migration - already using UTC timestamps
|
- Version > 26.2.6 → timestamps already UTC → skip
|
||||||
- Version < 26.2.6: Run migration - convert local timestamps to UTC
|
- Missing / unknown / unparsable version → migrate
|
||||||
|
- Migration flag present → skip
|
||||||
Affected tables:
|
- Detection says already UTC → skip
|
||||||
- Devices: devFirstConnection, devLastConnection, devLastNotification
|
|
||||||
- Events: eve_DateTime
|
|
||||||
- Sessions: ses_DateTimeConnection, ses_DateTimeDisconnection
|
|
||||||
- Notifications: DateTimeCreated, DateTimePushed
|
|
||||||
- Online_History: Scan_Date
|
|
||||||
- Plugins_Objects: DateTimeCreated, DateTimeChanged
|
|
||||||
- Plugins_Events: DateTimeCreated, DateTimeChanged
|
|
||||||
- Plugins_History: DateTimeCreated, DateTimeChanged
|
|
||||||
- AppEvents: DateTimeCreated
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if migration completed or wasn't needed, False on error
|
bool: True if migration completed or not needed, False on error
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
import conf
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
import datetime as dt
|
|
||||||
|
|
||||||
# Check VERSION from Settings table (from previous app run)
|
try:
|
||||||
sql.execute("SELECT setValue FROM Settings WHERE setKey = 'VERSION'")
|
# -------------------------------------------------
|
||||||
|
# Check migration flag (idempotency protection)
|
||||||
|
# -------------------------------------------------
|
||||||
|
try:
|
||||||
|
sql.execute("SELECT setValue FROM Settings WHERE setKey='DB_TIMESTAMPS_UTC_MIGRATED'")
|
||||||
|
result = sql.fetchone()
|
||||||
|
if result and str(result[0]) == "1":
|
||||||
|
mylog("verbose", "[db_upgrade] UTC timestamp migration already completed - skipping")
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
# Read previous version
|
||||||
|
# -------------------------------------------------
|
||||||
|
sql.execute("SELECT setValue FROM Settings WHERE setKey='VERSION'")
|
||||||
result = sql.fetchone()
|
result = sql.fetchone()
|
||||||
prev_version = result[0] if result else ""
|
prev_version = result[0] if result else ""
|
||||||
|
|
||||||
# Fresh install: VERSION is empty → timestamps already UTC from timeNowUTC()
|
mylog("verbose", f"[db_upgrade] Version '{prev_version}' detected.")
|
||||||
if not prev_version or prev_version == "" or prev_version == "unknown":
|
|
||||||
mylog("verbose", "[db_upgrade] Fresh install detected - timestamps already in UTC format")
|
# Default behaviour: migrate unless proven safe
|
||||||
|
should_migrate = True
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
# Version-based safety check
|
||||||
|
# -------------------------------------------------
|
||||||
|
if prev_version and str(prev_version).lower() != "unknown":
|
||||||
|
try:
|
||||||
|
version_parts = prev_version.lstrip('v').split('.')
|
||||||
|
major = int(version_parts[0]) if len(version_parts) > 0 else 0
|
||||||
|
minor = int(version_parts[1]) if len(version_parts) > 1 else 0
|
||||||
|
patch = int(version_parts[2]) if len(version_parts) > 2 else 0
|
||||||
|
|
||||||
|
# UTC timestamps introduced AFTER v26.2.6
|
||||||
|
if (major, minor, patch) > (26, 2, 6):
|
||||||
|
should_migrate = False
|
||||||
|
mylog(
|
||||||
|
"verbose",
|
||||||
|
f"[db_upgrade] Version {prev_version} confirmed UTC timestamps - skipping migration",
|
||||||
|
)
|
||||||
|
|
||||||
|
except (ValueError, IndexError) as e:
|
||||||
|
mylog(
|
||||||
|
"warn",
|
||||||
|
f"[db_upgrade] Could not parse version '{prev_version}': {e} - running migration as safety measure",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mylog(
|
||||||
|
"warn",
|
||||||
|
"[db_upgrade] VERSION missing/unknown - running migration as safety measure",
|
||||||
|
)
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
# Detection fallback
|
||||||
|
# -------------------------------------------------
|
||||||
|
if should_migrate:
|
||||||
|
try:
|
||||||
|
if is_timestamps_in_utc(sql):
|
||||||
|
mylog(
|
||||||
|
"verbose",
|
||||||
|
"[db_upgrade] Timestamps appear already UTC - skipping migration",
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
mylog(
|
||||||
|
"warn",
|
||||||
|
f"[db_upgrade] UTC detection failed ({e}) - continuing with migration",
|
||||||
|
)
|
||||||
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Parse version - format: "26.2.6" or "v26.2.6"
|
|
||||||
try:
|
|
||||||
version_parts = prev_version.strip('v').split('.')
|
|
||||||
major = int(version_parts[0]) if len(version_parts) > 0 else 0
|
|
||||||
minor = int(version_parts[1]) if len(version_parts) > 1 else 0
|
|
||||||
patch = int(version_parts[2]) if len(version_parts) > 2 else 0
|
|
||||||
|
|
||||||
# UTC timestamps introduced in v26.2.6
|
|
||||||
# If upgrading from 26.2.6 or later, timestamps are already UTC
|
|
||||||
if (major > 26) or (major == 26 and minor > 2) or (major == 26 and minor == 2 and patch >= 6):
|
|
||||||
mylog("verbose", f"[db_upgrade] Version {prev_version} already uses UTC timestamps - skipping migration")
|
|
||||||
return True
|
|
||||||
|
|
||||||
mylog("verbose", f"[db_upgrade] Upgrading from {prev_version} (< v26.2.6) - migrating timestamps to UTC")
|
|
||||||
|
|
||||||
except (ValueError, IndexError) as e:
|
|
||||||
mylog("warn", f"[db_upgrade] Could not parse version '{prev_version}': {e} - checking timestamps")
|
|
||||||
# Fallback: use detection logic
|
|
||||||
if is_timestamps_in_utc(sql):
|
|
||||||
mylog("verbose", "[db_upgrade] Timestamps appear to be in UTC - skipping migration")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Get timezone offset
|
# Get timezone offset
|
||||||
try:
|
try:
|
||||||
if isinstance(conf.tz, dt.tzinfo):
|
if isinstance(conf.tz, dt.tzinfo):
|
||||||
|
|||||||
@@ -49,6 +49,18 @@ class PluginObjectInstance:
|
|||||||
"SELECT * FROM Plugins_Objects WHERE Plugin = ?", (plugin,)
|
"SELECT * FROM Plugins_Objects WHERE Plugin = ?", (plugin,)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def getLastNCreatedPerPLugin(self, plugin, entries=1):
|
||||||
|
return self._fetchall(
|
||||||
|
"""
|
||||||
|
SELECT *
|
||||||
|
FROM Plugins_Objects
|
||||||
|
WHERE Plugin = ?
|
||||||
|
ORDER BY DateTimeCreated DESC
|
||||||
|
LIMIT ?
|
||||||
|
""",
|
||||||
|
(plugin, entries),
|
||||||
|
)
|
||||||
|
|
||||||
def getByField(self, plugPrefix, matchedColumn, matchedKey, returnFields=None):
|
def getByField(self, plugPrefix, matchedColumn, matchedKey, returnFields=None):
|
||||||
rows = self._fetchall(
|
rows = self._fetchall(
|
||||||
f"SELECT * FROM Plugins_Objects WHERE Plugin = ? AND {matchedColumn} = ?",
|
f"SELECT * FROM Plugins_Objects WHERE Plugin = ? AND {matchedColumn} = ?",
|
||||||
|
|||||||
Reference in New Issue
Block a user