Add in two tables on header, sort out permission and ownership of params file

This commit is contained in:
2025-09-04 10:04:25 +01:00
parent d94bf8e033
commit 2dd3d234df
4 changed files with 102 additions and 27 deletions

View File

@@ -305,3 +305,43 @@ p.cssvalid,p.htmlvalid {float:left;margin-right:20px}
.mailstats-detail a:hover { .mailstats-detail a:hover {
text-decoration: underline; 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;
}
}

View File

@@ -16,9 +16,30 @@
<br /> <br />
<h2>${structure:title}</h2> <h2>${structure:title}</h2>
<br /> <br />
<div class="headerpanel"> <div class="emailstatus-wrapper">
<div class = "innerheaderpanel"> <h2 class="emailstatus-header">Email System Status</h2>
<!---Add in header information here --> <div class="emailstatus-tablecontainer">
<!-- Table 1 -->
<table class="emailstatus-table">
<thead>
<tr>
<th colspan="2">Security & Filtering</th>
</tr>
</thead>
<tbody>
<!---Add in table1 information here -->
</tbody>
</table>
<table class="emailstatus-table">
<thead>
<tr>
<th colspan="2">Mail Traffic Statistics</th>
</tr>
</thead>
<tbody>
<!---Add in table2 information here -->
</tbody>
</table>
</div> </div>
</div> </div>
<br /> <br />

View File

@@ -1020,6 +1020,9 @@ def replace_between(text, start, end, replacement):
replaced_text = re.sub(pattern, replacement, text, flags=re.DOTALL) replaced_text = re.sub(pattern, replacement, text, flags=re.DOTALL)
return replaced_text return replaced_text
def assemble_heading_row(label,value):
return f"<tr><td>{label}</td><td>{value}</td><tr>"
def get_heading(): def get_heading():
# #
# Needs from anaytsis # Needs from anaytsis
@@ -1037,44 +1040,50 @@ def get_heading():
# Clam Version/DB Count/Last DB update # Clam Version/DB Count/Last DB update
clam_output = subprocess.getoutput("freshclam -V") 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 # SpamAssassin Version
sa_output = subprocess.getoutput("spamassassin -V") 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 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 connection stats
smtp_stats = f"External SMTP connections accepted: {totalexternalsmtpsessions}\n"\ smtp_stats = assemble_heading_row("External SMTP connections accepted:",totalexternalsmtpsessions)
f"Internal SMTP connections accepted: {totalinternalsmtpsessions}" smtp_stats += assemble_heading_row("Internal SMTP connections accepted:",totalinternalsmtpsessions)
if len(connection_type_counts)>0: if len(connection_type_counts)>0:
for connection_type in connection_type_counts.keys(): 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: if len(total_ports)>0:
for port_number in total_ports.keys(): 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"\ rows = [
f"Average spam score (accepted): {spamavg or 0:.2f}\n"\ assemble_heading_row("Emails per hour:", f"{(emailperhour if emailperhour is not None else 0):.1f}/hr"),
f"Average spam score (rejected): {rejectspamavg or 0:.2f}\n"\ assemble_heading_row("Average spam score (accepted):", f"{(spamavg if spamavg is not None else 0):.2f}"),
f"Average ham score: {hamavg or 0:.2f}\n"\ assemble_heading_row("Average spam score (rejected):", f"{(rejectspamavg if rejectspamavg is not None else 0):.2f}"),
f"Number of DMARC reporting emails sent: {DMARCSendCount or 0} (not shown on table)" 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 doesnt add its own newline
# DMARC approved emails # DMARC approved emails
dmarc_info = "" dmarc_info = ""
if hamcount != 0: if hamcount != 0:
dmarc_ok_percentage = DMARCOkCount * 100 / hamcount 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 # Accumulate all strings
header_str = "\n".join([clam_info, sa_info, tag_reject_info, smtp_stats, dmarc_info]) #header_str = "<br />".join([clam_info, sa_info, tag_reject_info, smtp_stats, dmarc_info])
# switch newlines to <br /> # switch newlines to <br />
header_str = header_str.replace("\n","<br />") #header_str = header_str.replace("\n","<br />")
return header_str header_str1 = clam_info + sa_info + tag_reject_info
header_str2 = smtp_stats + dmarc_info
return header_str1,header_str2
def scan_mail_users(): def scan_mail_users():
# #
@@ -1181,7 +1190,7 @@ def extract_blacklist_domain(text):
""" """
s = text if isinstance(text, str) else str(text or "") s = text if isinstance(text, str) else str(text or "")
s_lower = s.lower() s_lower = s.lower()
logging.info(f"extract blacklist called:{text}") logging.debug(f"extract blacklist called:{text}")
combined = ",".join([RBLList, SBLList, UBLList]) combined = ",".join([RBLList, SBLList, UBLList])
@@ -1213,7 +1222,7 @@ def extract_blacklist_domain(text):
for part in combined.split(","): for part in combined.split(","):
entry = part.strip() entry = part.strip()
logging.info(f"Comparing: {entry}") logging.debug(f"Comparing: {entry}")
if not entry: if not entry:
continue continue
@@ -1868,8 +1877,10 @@ if __name__ == "__main__":
total_html = rendered_html total_html = rendered_html
# Add in the header information # Add in the header information
header_rendered_html = get_heading() header_rendered_html1,header_rendered_html2 = get_heading()
total_html = insert_string_after(total_html,header_rendered_html, "<!---Add in header information here -->") total_html = insert_string_after(total_html,header_rendered_html1, "<!---Add in table1 information here -->")
total_html = insert_string_after(total_html,header_rendered_html2, "<!---Add in table2 information here -->")
header_rendered_html = header_rendered_html1 + header_rendered_html2
#add in the subservient tables..(remeber they appear in the reverse order of below!) #add in the subservient tables..(remeber they appear in the reverse order of below!)

View File

@@ -28,6 +28,7 @@ Requires: python3-chameleon
Requires: python3-mysql Requires: python3-mysql
Requires: python3-matplotlib Requires: python3-matplotlib
Requires: python3-pip Requires: python3-pip
Requires: systemd-libs
AutoReqProv: no AutoReqProv: no
%description %description
@@ -42,8 +43,8 @@ See https://wiki.koozali.org/mailstats
- Fix up CSS for Summary Page [SME: 13121] - Fix up CSS for Summary Page [SME: 13121]
- Get Detail logs page working and prettyfy [SME: 13121] - Get Detail logs page working and prettyfy [SME: 13121]
- Add in C wrapper source code to interrogate journal [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 <brianr@koozali.org> 11.1-4.sme * Mon Sep 01 2025 Brian Read <brianr@koozali.org> 11.1-4.sme
- More fixes for Journal bytes instead of characters [SME: 13117] - More fixes for Journal bytes instead of characters [SME: 13117]
@@ -138,8 +139,9 @@ perl createlinks
/bin/rm -rf $RPM_BUILD_ROOT /bin/rm -rf $RPM_BUILD_ROOT
(cd root ; /usr/bin/find . -depth -print | /bin/cpio -dump $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 chmod +x $RPM_BUILD_ROOT/usr/bin/runmailstats.sh
chown root:root $RPM_BUILD_ROOT/etc/mailstats/dp.php #chmod 0640 $RPM_BUILD_ROOT/etc/mailstats/db.php
chmod 0600 $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 # Define the placeholder and generate the current date and time
now=$(date +"%Y-%m-%d %H:%M:%S") 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 %files -f %{name}-%{version}-filelist
%defattr(-,root,root) %defattr(-,root,root)
%attr(0640, root, apache) %config(noreplace) /etc/mailstats/db.php