From 2dd3d234df8af0196d6c1c145d86d8efd628a057 Mon Sep 17 00:00:00 2001 From: Brian Read Date: Thu, 4 Sep 2025 10:04:25 +0100 Subject: [PATCH] Add in two tables on header, sort out permission and ownership of params file --- root/opt/mailstats/css/mailstats.css | 40 +++++++++++++++ .../opt/mailstats/templates/mailstats.html.pt | 27 ++++++++-- root/usr/bin/mailstats.py | 51 +++++++++++-------- smeserver-mailstats.spec | 11 ++-- 4 files changed, 102 insertions(+), 27 deletions(-) diff --git a/root/opt/mailstats/css/mailstats.css b/root/opt/mailstats/css/mailstats.css index 9634386..171d07e 100644 --- a/root/opt/mailstats/css/mailstats.css +++ b/root/opt/mailstats/css/mailstats.css @@ -304,4 +304,44 @@ p.cssvalid,p.htmlvalid {float:left;margin-right:20px} .mailstats-detail a:hover { text-decoration: underline; +} + +/* ============================================== + Status header at top of table (scoped under emailstatus) + ============================================== */ +.emailstatus-wrapper { + font-family: Arial, sans-serif; + padding: 20px; +} +.emailstatus-header { + text-align: center; + margin-bottom: 20px; +} +.emailstatus-tablecontainer { + display: flex; + gap: 20px; + flex-wrap: wrap; +} +.emailstatus-table { + border-collapse: collapse; + min-width: 300px; + flex: 1 1 45%; +} +.emailstatus-table th { + background-color: #a9a9a9; + color: black; + text-align: left; + padding: 8px; +} +.emailstatus-table td { + padding: 8px; + border: 1px solid #ddd; +} +.emailstatus-table tr:nth-child(even) { + background-color: #f9f9f9; +} +@media (max-width: 768px) { + .emailstatus-tablecontainer { + flex-direction: column; + } } \ No newline at end of file diff --git a/root/opt/mailstats/templates/mailstats.html.pt b/root/opt/mailstats/templates/mailstats.html.pt index e9310da..18e0bdf 100644 --- a/root/opt/mailstats/templates/mailstats.html.pt +++ b/root/opt/mailstats/templates/mailstats.html.pt @@ -16,9 +16,30 @@

${structure:title}


-
-
- +
+

Email System Status

+
+ + + + + + + + + + +
Security & Filtering
+ + + + + + + + + +
Mail Traffic Statistics

diff --git a/root/usr/bin/mailstats.py b/root/usr/bin/mailstats.py index 9736c8e..f295c32 100644 --- a/root/usr/bin/mailstats.py +++ b/root/usr/bin/mailstats.py @@ -1020,6 +1020,9 @@ def replace_between(text, start, end, replacement): replaced_text = re.sub(pattern, replacement, text, flags=re.DOTALL) return replaced_text +def assemble_heading_row(label,value): + return f"{label}{value}" + def get_heading(): # # Needs from anaytsis @@ -1037,44 +1040,50 @@ def get_heading(): # Clam Version/DB Count/Last DB update clam_output = subprocess.getoutput("freshclam -V") - clam_info = f"Clam Version/DB Count/Last DB update: {clam_output}" + clam_info = assemble_heading_row("Clam Version/DB Count/Last DB update:", clam_output) # SpamAssassin Version sa_output = subprocess.getoutput("spamassassin -V") - sa_info = f"SpamAssassin Version: {sa_output}" + sa_info = assemble_heading_row("SpamAssassin Version: ",sa_output) # Tag level and Reject level - tag_reject_info = f"Tag level: {SATagLevel}; Reject level: {SARejectLevel} {warnnoreject}" + tag_reject_info = assemble_heading_row("Tag level:",SATagLevel) + tag_reject_info += assemble_heading_row("Reject level: ",f"{SARejectLevel} {warnnoreject}") # SMTP connection stats - smtp_stats = f"External SMTP connections accepted: {totalexternalsmtpsessions}\n"\ - f"Internal SMTP connections accepted: {totalinternalsmtpsessions}" + smtp_stats = assemble_heading_row("External SMTP connections accepted:",totalexternalsmtpsessions) + smtp_stats += assemble_heading_row("Internal SMTP connections accepted:",totalinternalsmtpsessions) if len(connection_type_counts)>0: for connection_type in connection_type_counts.keys(): - smtp_stats += f"\nCount of {connection_type} connections: {connection_type_counts[connection_type]}" + smtp_stats += assemble_heading_row(f"\nCount of {connection_type} connections:",connection_type_counts[connection_type]) if len(total_ports)>0: for port_number in total_ports.keys(): - smtp_stats += f"\nCount of port {port_number} connections: {total_ports[port_number]}" + smtp_stats += assemble_heading_row(f"\nCount of port {port_number} connections: ",total_ports[port_number]) - smtp_stats = smtp_stats + f"\nEmails per hour: {emailperhour:.1f}/hr\n"\ - f"Average spam score (accepted): {spamavg or 0:.2f}\n"\ - f"Average spam score (rejected): {rejectspamavg or 0:.2f}\n"\ - f"Average ham score: {hamavg or 0:.2f}\n"\ - f"Number of DMARC reporting emails sent: {DMARCSendCount or 0} (not shown on table)" + rows = [ + assemble_heading_row("Emails per hour:", f"{(emailperhour if emailperhour is not None else 0):.1f}/hr"), + assemble_heading_row("Average spam score (accepted):", f"{(spamavg if spamavg is not None else 0):.2f}"), + assemble_heading_row("Average spam score (rejected):", f"{(rejectspamavg if rejectspamavg is not None else 0):.2f}"), + assemble_heading_row("Average ham score:", f"{(hamavg if hamavg is not None else 0):.2f}"), + assemble_heading_row("Number of DMARC reporting emails sent:", f"{DMARCSendCount if DMARCSendCount is not None else 0} (not shown on table)"), + ] + smtp_stats += " ".join(rows) # or "\n".join(rows) if assemble_heading_row doesn’t add its own newline # DMARC approved emails dmarc_info = "" if hamcount != 0: dmarc_ok_percentage = DMARCOkCount * 100 / hamcount - dmarc_info = f"Number of emails approved through DMARC: {DMARCOkCount or 0} ({dmarc_ok_percentage:.2f}% of Ham count)" + dmarc_info = assemble_heading_row("Number of emails approved through DMARC:",f"{DMARCOkCount or 0} ({dmarc_ok_percentage:.2f}% of Ham count)") # Accumulate all strings - header_str = "\n".join([clam_info, sa_info, tag_reject_info, smtp_stats, dmarc_info]) + #header_str = "
".join([clam_info, sa_info, tag_reject_info, smtp_stats, dmarc_info]) # switch newlines to
- header_str = header_str.replace("\n","
") - return header_str + #header_str = header_str.replace("\n","
") + header_str1 = clam_info + sa_info + tag_reject_info + header_str2 = smtp_stats + dmarc_info + return header_str1,header_str2 def scan_mail_users(): # @@ -1181,7 +1190,7 @@ def extract_blacklist_domain(text): """ s = text if isinstance(text, str) else str(text or "") s_lower = s.lower() - logging.info(f"extract blacklist called:{text}") + logging.debug(f"extract blacklist called:{text}") combined = ",".join([RBLList, SBLList, UBLList]) @@ -1213,7 +1222,7 @@ def extract_blacklist_domain(text): for part in combined.split(","): entry = part.strip() - logging.info(f"Comparing: {entry}") + logging.debug(f"Comparing: {entry}") if not entry: continue @@ -1868,8 +1877,10 @@ if __name__ == "__main__": total_html = rendered_html # Add in the header information - header_rendered_html = get_heading() - total_html = insert_string_after(total_html,header_rendered_html, "") + header_rendered_html1,header_rendered_html2 = get_heading() + total_html = insert_string_after(total_html,header_rendered_html1, "") + total_html = insert_string_after(total_html,header_rendered_html2, "") + header_rendered_html = header_rendered_html1 + header_rendered_html2 #add in the subservient tables..(remeber they appear in the reverse order of below!) diff --git a/smeserver-mailstats.spec b/smeserver-mailstats.spec index 51d9474..418334b 100644 --- a/smeserver-mailstats.spec +++ b/smeserver-mailstats.spec @@ -28,6 +28,7 @@ Requires: python3-chameleon Requires: python3-mysql Requires: python3-matplotlib Requires: python3-pip +Requires: systemd-libs AutoReqProv: no %description @@ -42,8 +43,8 @@ See https://wiki.koozali.org/mailstats - Fix up CSS for Summary Page [SME: 13121] - Get Detail logs page working and prettyfy [SME: 13121] - Add in C wrapper source code to interrogate journal [SME: 13121] - - +- Get permission and ownership right for /etc/mailstats/db.php [SME: 13121] +- Refactor main table header into two tables side by side [SME: 13121] * Mon Sep 01 2025 Brian Read 11.1-4.sme - More fixes for Journal bytes instead of characters [SME: 13117] @@ -138,8 +139,9 @@ perl createlinks /bin/rm -rf $RPM_BUILD_ROOT (cd root ; /usr/bin/find . -depth -print | /bin/cpio -dump $RPM_BUILD_ROOT) chmod +x $RPM_BUILD_ROOT/usr/bin/runmailstats.sh -chown root:root $RPM_BUILD_ROOT/etc/mailstats/dp.php -chmod 0600 $RPM_BUILD_ROOT/etc/mailstats/db.php +#chmod 0640 $RPM_BUILD_ROOT/etc/mailstats/db.php +#ls -l /builddir/build/BUILDROOT/smeserver-mailstats-11.1-5.el8.sme.x86_64/etc/mailstats/ +#chown root:102 $RPM_BUILD_ROOT/etc/mailstats/db.php # Define the placeholder and generate the current date and time now=$(date +"%Y-%m-%d %H:%M:%S") @@ -159,3 +161,4 @@ sed -i "s|__BUILD_DATE_TIME__|$now|" $RPM_BUILD_ROOT/usr/bin/mailstats.py %files -f %{name}-%{version}-filelist %defattr(-,root,root) +%attr(0640, root, apache) %config(noreplace) /etc/mailstats/db.php