Sort out DB params access for mailstats, remove DB config from SM2

This commit is contained in:
2025-09-07 09:18:39 +01:00
parent 88bc38adf3
commit 52b33e166a
3 changed files with 77 additions and 33 deletions

View File

@@ -454,23 +454,28 @@ def create_graph(data_dict, graph_type="line", output_file="graph.png",iso_date=
# return data # return data
def save_summaries_to_db(cursor, conn, date_str, hour, parsed_data): def save_summaries_to_db(cursor, conn, date_str, hour, parsed_data):
# Convert parsed_data to JSON string
global count_records_to_db global count_records_to_db
json_data = json.dumps(parsed_data) json_data = json.dumps(parsed_data)
# Insert the record
insert_query = """ insert_query = """
INSERT INTO SummaryLogs (Date, Hour, logData) INSERT INTO SummaryLogs (Date, Hour, logData)
VALUES (%s, %s, %s) VALUES (%s, %s, %s)
""" """
try: try:
# Check if the cursor is open (pymysql has no explicit is_closed; handle by try/except)
cursor.execute(insert_query, (date_str, hour, json_data)) cursor.execute(insert_query, (date_str, hour, json_data))
conn.commit() conn.commit()
count_records_to_db += 1 count_records_to_db += 1
except pymysql.Error as err: except pymysql.Error as err:
logging.error(f"DB Error {date_str} {hour} : {err}") # Handle cursor closed or other DB errors
if 'closed' in str(err).lower():
logging.error(f"DB Error {date_str} {hour} : Cursor is closed. Check connection handling.")
else:
logging.error(f"DB Error {date_str} {hour} : {err}")
conn.rollback() conn.rollback()
except Exception as ex:
logging.error(f"Unexpected DB Error {date_str} {hour} : {ex}")
conn.rollback()
def is_running_under_thonny(): def is_running_under_thonny():
# Check for the 'THONNY_USER_DIR' environment variable # Check for the 'THONNY_USER_DIR' environment variable
@@ -1251,6 +1256,41 @@ def format_duration(seconds: float) -> str:
return str(timedelta(seconds=seconds)) return str(timedelta(seconds=seconds))
DB_CONFIG_PATH = '/etc/mailstats/db.php'
def parse_php_config(path):
# Read file as text and extract key-value pairs using regex
try:
with open(path, 'r') as f:
content = f.read()
cfg = {}
for match in re.finditer(r"'(\w+)'\s*=>\s*'([^']*)'", content):
cfg[match.group(1)] = match.group(2)
return cfg
except Exception as e:
logging.error(f"Could not parse PHP config file: {e}")
return {}
def load_db_config():
db_host = os.environ.get('MAILSTATS_DB_HOST', 'localhost')
db_user = os.environ.get('MAILSTATS_DB_USER', '')
db_pass = os.environ.get('MAILSTATS_DB_PASS', '')
db_name = os.environ.get('MAILSTATS_DB_NAME', '')
if db_user == '' or db_pass == '' or db_name == '':
if os.path.isfile(DB_CONFIG_PATH) and os.access(DB_CONFIG_PATH, os.R_OK):
cfg = parse_php_config(DB_CONFIG_PATH)
db_host = cfg.get('host', db_host)
db_user = cfg.get('user', db_user)
db_pass = cfg.get('pass', db_pass)
db_name = cfg.get('name', db_name)
if db_user == '' or db_pass == '' or db_name == '':
logging.error('DB credentials missing (env and config file).')
raise RuntimeError('DB credentials missing (env and config file)')
return db_host, db_user, db_pass, db_name
if __name__ == "__main__": if __name__ == "__main__":
start_time = datetime.now() start_time = datetime.now()
try: try:
@@ -1336,16 +1376,15 @@ if __name__ == "__main__":
# Db save control # Db save control
saveData = get_value(ConfigDB,"mailstats","SaveDataToMySQL","no") == 'yes' or forceDbSave saveData = get_value(ConfigDB,"mailstats","SaveDataToMySQL","no") == 'yes' or forceDbSave
logging.debug(f"Save Mailstats to DB set:{saveData} ") logging.debug(f"Save Mailstats to DB set:{saveData} ")
if saveData: if saveData:
# Connect to MySQL DB for saving # Database config retrieval
DBName = "mailstats" try:
DBHost = get_value(ConfigDB, 'mailstats', 'DBHost', "localhost") DBHost, DBUser, DBPassw, DBName = load_db_config()
DBPort = int(get_value(ConfigDB, 'mailstats', 'DBPort', "3306")) # Ensure port is an integer DBPort = 3306 # If you want configurability, load this from config too
DBPassw = 'mailstats' UnixSocket = "/var/lib/mysql/mysql.sock"
DBUser = 'mailstats' except RuntimeError as err:
UnixSocket = "/var/lib/mysql/mysql.sock" logging.error(f"Database config error: {err}")
saveData = False
# Try to establish a database connection # Try to establish a database connection
try: try:
conn = pymysql.connect( conn = pymysql.connect(
@@ -1355,7 +1394,7 @@ if __name__ == "__main__":
database=DBName, database=DBName,
port=DBPort, port=DBPort,
unix_socket=UnixSocket, unix_socket=UnixSocket,
cursorclass=pymysql.cursors.DictCursor # Optional: use DictCursor for dict output cursorclass=pymysql.cursors.DictCursor
) )
cursor = conn.cursor() cursor = conn.cursor()
# Check if the table exists before creating it # Check if the table exists before creating it
@@ -1363,31 +1402,34 @@ if __name__ == "__main__":
cursor.execute(check_table_query) cursor.execute(check_table_query)
table_exists = cursor.fetchone() table_exists = cursor.fetchone()
if not table_exists: if not table_exists:
# Create table if it doesn't exist
cursor.execute(""" cursor.execute("""
CREATE TABLE IF NOT EXISTS SummaryLogs ( CREATE TABLE IF NOT EXISTS SummaryLogs (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
Date DATE, Date DATE,
Hour INT, Hour INT,
logData TEXT logData TEXT
) )
""") """)
# Delete existing records for the given date # Delete existing records for the given date
try: try:
delete_query = """ delete_query = """
DELETE FROM SummaryLogs DELETE FROM SummaryLogs
WHERE Date = %s WHERE Date = %s
""" """
cursor.execute(delete_query, (analysis_date,)) # Don't forget the extra comma for tuple cursor.execute(delete_query, (analysis_date,))
# Get the number of records deleted
rows_deleted = cursor.rowcount rows_deleted = cursor.rowcount
if rows_deleted > 0: if rows_deleted > 0:
logging.debug(f"Deleted {rows_deleted} rows for {analysis_date} ") logging.debug(f"Deleted {rows_deleted} rows for {analysis_date}")
except pymysql.Error as e: except pymysql.Error as e:
logging.error(f"SQL Delete failed ({delete_query}) ({e}) ") logging.error(f"SQL Delete failed ({delete_query}) ({e})")
# Commit changes & close resources after all DB operations
conn.commit()
#cursor.close()
#conn.close()
except pymysql.Error as e: except pymysql.Error as e:
logging.error(f"Unable to connect to {DBName} on {DBHost} port {DBPort} error ({e}) ") logging.error(f"Unable to connect to {DBName} on {DBHost} port {DBPort} error ({e})")
saveData = False saveData = False
nolinks = not saveData nolinks = not saveData

View File

@@ -77,7 +77,7 @@
<div class=dbwanted> <div class=dbwanted>
<!--
<h2 class='subh2'><%=l('mst_Details_for_connection_to_database')%></h2> <h2 class='subh2'><%=l('mst_Details_for_connection_to_database')%></h2>
<p><span class=label> <p><span class=label>
@@ -108,7 +108,7 @@
% param 'DBPassword' => $mst_data->{DBPassword} unless param 'DBPassword'; % param 'DBPassword' => $mst_data->{DBPassword} unless param 'DBPassword';
%=password_field 'DBPassword', class => 'pass13 sme-password', autocomplete => 'off' %=password_field 'DBPassword', class => 'pass13 sme-password', autocomplete => 'off'
</span></p> </span></p>
-->
</div> </div>

View File

@@ -92,6 +92,8 @@ usermod -aG systemd-journal www
%changelog %changelog
* Thu Sep 04 2025 Brian Read <brianr@koozali.org> 11.1-6.sme * Thu Sep 04 2025 Brian Read <brianr@koozali.org> 11.1-6.sme
- Add favicon to mailstats table, summary and detailed pages [SME: 13121] - Add favicon to mailstats table, summary and detailed pages [SME: 13121]
- Bring DB config reading for mailstats itself inline with php summary and detailed logs - using /etc/mailstats/db.php
- Remove DB config fields from the SM2 config panel
* Tue Sep 02 2025 Brian Read <brianr@koozali.org> 11.1-5.sme * Tue Sep 02 2025 Brian Read <brianr@koozali.org> 11.1-5.sme
- Speed up Journal access [SME: 13121] - Speed up Journal access [SME: 13121]