Files
NetAlertX/front/plugins/maintenance/maintenance.py
2026-06-14 10:35:08 +10:00

141 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python
import os
import sys
# Register NetAlertX directories
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from const import logPath # noqa: E402 [flake8 lint suppression]
from messaging.in_app import remove_old # noqa: E402 [flake8 lint suppression]
from utils.datetime_utils import timeNowUTC # noqa: E402 [flake8 lint suppression]
import conf # noqa: E402 [flake8 lint suppression]
from pytz import timezone # noqa: E402 [flake8 lint suppression]
# Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE'))
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
pluginName = 'MAINT'
LOG_PATH = logPath + '/plugins'
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
def main():
mylog('verbose', [f'[{pluginName}] In script'])
MAINT_LOG_LENGTH = int(get_setting_value('MAINT_LOG_LENGTH'))
MAINT_NOTI_LENGTH = int(get_setting_value('MAINT_NOTI_LENGTH'))
logFiles = ["app.log", "nginx-error.log"]
# Check if set
if MAINT_LOG_LENGTH != 0:
MAX_TAIL_SIZE = 0.1 * 1024 * 1024 # 0.1 MB
for fileEntry in logFiles:
logFile = os.path.join(logPath, fileEntry)
if not os.path.isfile(logFile):
mylog('verbose', [f'[{pluginName}] File not found: {fileEntry}'])
continue
size_before = os.path.getsize(logFile)
mylog('verbose', [f'[{pluginName}] {fileEntry} size BEFORE: {size_before}'])
try:
if size_before > MAX_TAIL_SIZE:
mylog('verbose', [f'[{pluginName}] {fileEntry} exceeds {MAX_TAIL_SIZE} bytes, truncating'])
with open(logFile, 'r+b') as f:
f.truncate(0)
else:
lines_to_keep = tail_file(logFile, MAINT_LOG_LENGTH)
with open(logFile, 'r+b') as f:
f.seek(0)
f.truncate()
f.writelines(lines_to_keep)
size_after = os.path.getsize(logFile)
mylog('verbose', [f'[{pluginName}] {fileEntry} size AFTER: {size_after}'])
except Exception as e:
mylog('none', [f'[{pluginName}] Failed to clean {fileEntry}: {e}'])
# Check if set
if MAINT_NOTI_LENGTH != 0:
mylog('verbose', [f'[{pluginName}] Cleaning in-app notification history'])
remove_old(MAINT_NOTI_LENGTH)
# Delete processed sync artefact files older than 24 hours.
# These are created by the SYNC plugin (Mode 3) when it renames received
# device JSON files to processed_*.log after processing. They have no value
# once processed and are not cleaned up anywhere else.
_PROCESSED_MAX_AGE_SECS = 24 * 3600
now = timeNowUTC(as_string=False).timestamp()
deleted = 0
for fname in os.listdir(LOG_PATH):
if fname.startswith('processed_') and fname.endswith('.log'):
fpath = os.path.join(LOG_PATH, fname)
if os.path.isfile(fpath) and (now - os.path.getmtime(fpath)) > _PROCESSED_MAX_AGE_SECS:
os.remove(fpath)
deleted += 1
mylog('verbose', [f'[{pluginName}] Deleted {deleted} processed sync artefact file(s) from {LOG_PATH}'])
return 0
def tail_file(filepath, num_lines):
"""
Return the last num_lines lines from a file without reading the entire file.
"""
if num_lines <= 0:
return []
block_size = 8192
with open(filepath, 'rb') as f:
f.seek(0, os.SEEK_END)
file_size = f.tell()
blocks = []
lines_found = 0
position = file_size
while position > 0 and lines_found <= num_lines:
read_size = min(block_size, position)
position -= read_size
f.seek(position)
block = f.read(read_size)
blocks.append(block)
lines_found += block.count(b'\n')
data = b''.join(reversed(blocks))
return data.splitlines(keepends=True)[-num_lines:]
# ===============================================================================
# BEGIN
# ===============================================================================
if __name__ == '__main__':
main()