Scheduled reports: Download, a report for a period as a ZIP
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Canceled after 1m19s

inbuxa:ReportExport/set creates {reportId, from?, to?}: the report built
for that period (its last full one by default, at most 90 days back, as
far as metrics are kept) as a ZIP of summary.txt and the section CSVs,
stored as an upload, mailing nobody. Reading needs sysScheduledReportGet,
as seeing the report does; a tenant administrator downloads only their
own; every download is in the audit log. A download doesn't move the
deliverability baseline the next mail compares with.
This commit is contained in:
jcoffey-dev committed 2026-10-06 15:24:34 -07:00
1 parent ff5480cb55
commit 09ccad4b4b
16 files changed
+494 -3

No files matched your search

@@ -1087,6 +1087,36 @@ async fn certificates(
}))
}
/// RP-19: the report for a period as files, without mailing anyone: the
/// summary as text, and a CSV for each section that has rows. A download
/// doesn't move the deliverability baseline the next mail compares with.
pub async fn export_files(
server: &Server,
report: &Report,
from: u64,
to: u64,
) -> trc::Result<Vec<(String, Vec<u8>)>> {
let built = build(server, report, from, to).await?;
let mut summary = format!("{}\n{}\n\n", report.name, model::period_label(from, to));
if built.parts.is_empty() {
summary.push_str("Nothing to report for this period.\n");
}
for part in &built.parts {
summary.push_str(&format!("{}\n", part.title));
for line in &part.lines {
summary.push_str(&format!(" {line}\n"));
}
summary.push('\n');
}
let mut files = vec![("summary.txt".to_string(), summary.into_bytes())];
for part in built.parts {
if let Some((name, body)) = part.csv {
files.push((name, body.into_bytes()));
}
}
Ok(files)
}
// --- The mail -------------------------------------------------------------
fn escape(s: &str) -> String {