Update send-mail.ps1

This commit is contained in:
Mattia Tadini 2025-11-24 07:52:56 +00:00
parent 08e73cdebc
commit de09a2e069

View File

@ -100,8 +100,7 @@ function Build-ReportHtml {
if ($PSScriptRoot) {
$candidateDirs += (Join-Path $PSScriptRoot 'bin/conf')
}
elseif ($PSCommandPath) {
} elseif ($PSCommandPath) {
$candidateDirs += (Join-Path (Split-Path -Parent $PSCommandPath) 'bin/conf')
}
@ -116,14 +115,19 @@ function Build-ReportHtml {
}
}
# Fallback: invio testo semplice
if (-not $templatePath) {
Write-Log WARN "[MAIL] Template HTML non trovato. Invio in testo semplice."
$encoded = $Body -replace '&','&amp;' -replace '<','&lt;' -replace '>','&gt;'
$encoded = $encoded -replace "`r`n","<br />" -replace "`n","<br />"
@"
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>$Subject</title></head>
<head>
<meta charset="UTF-8">
<title>$Subject</title>
</head>
<body style="font-family: Tahoma, Arial, sans-serif; font-size: 12px;">
$encoded
</body>
@ -143,10 +147,14 @@ $encoded
if (-not $template) {
$encoded = $Body -replace '&','&amp;' -replace '<','&lt;' -replace '>','&gt;'
$encoded = $encoded -replace "`r`n","<br />" -replace "`n","<br />"
@"
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>$Subject</title></head>
<head>
<meta charset="UTF-8">
<title>$Subject</title>
</head>
<body style="font-family: Tahoma, Arial, sans-serif; font-size: 12px;">
$encoded
</body>
@ -158,54 +166,69 @@ $encoded
$statusInfo = Get-StatusInfo -Body $Body
# Host / agente
$agent = if ($HostName) { $HostName } elseif ($env:COMPUTERNAME) { $env:COMPUTERNAME } else { 'Sconosciuto' }
$agent = if ($HostName) {
$HostName
} elseif ($env:COMPUTERNAME) {
$env:COMPUTERNAME
} else {
'Sconosciuto'
}
# Versione script (se definita nel main, es: $ScriptVersion)
$scriptVersion = 'N/D'
try {
$sv = Get-Variable -Name ScriptVersion -ErrorAction SilentlyContinue
if ($sv) { $scriptVersion = $sv.Value }
} catch {}
} catch { }
# Parsing del body
$lines = $Body -split "(`r`n|`n)"
# --- Estrazione Start / Fine dal body tramite regex ---
$startTime = ''
$endTime = ''
$startLine = $lines | Where-Object { $_ -match 'Start:' } | Select-Object -First 1
$endLine = $lines | Where-Object { $_ -match 'Fine:' } | Select-Object -First 1
$startMatch = [regex]::Match($Body, 'Start:\s*(\d{2}-\d{2}-\d{4}\s+\d{2}:\d{2}:\d{2})')
if ($startMatch.Success) {
$startTime = $startMatch.Groups[1].Value.Trim()
}
$startTime = if ($startLine) { ($startLine -replace '.*Start:\s*','').Trim() } else { '' }
$endTime = if ($endLine) { ($endLine -replace '.*Fine:\s*','').Trim() } else { '' }
$endMatch = [regex]::Match($Body, 'Fine:\s*(\d{2}-\d{2}-\d{4}\s+\d{2}:\d{2}:\d{2})')
if ($endMatch.Success) {
$endTime = $endMatch.Groups[1].Value.Trim()
}
$backupDateTime = if ($endTime) { $endTime } elseif ($startTime) { $startTime } else { (Get-Date).ToString('dd/MM/yyyy HH:mm:ss') }
# Se "Fine:" non è ancora presente nel log passato, uso listante attuale
if (-not $endTime) {
$endTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss')
}
# Durata - provo sia con dd/MM/yyyy che dd-MM-yyyy
# Data/ora del report: uso lora di fine se disponibile, altrimenti quella di inizio
$backupDateTime = if ($endTime) {
$endTime
} elseif ($startTime) {
$startTime
} else {
(Get-Date).ToString('dd/MM/yyyy HH:mm:ss')
}
# --- Calcolo durata: Fine - Inizio ---
$durationStr = ''
try {
$culture = [System.Globalization.CultureInfo]::GetCultureInfo('it-IT')
$formats = @('dd/MM/yyyy HH:mm:ss','dd-MM-yyyy HH:mm:ss')
[datetime]$startDt = $null
[datetime]$endDt = $null
if ($startTime) {
foreach ($fmt in $formats) {
if ([datetime]::TryParseExact($startTime, $fmt, $culture, [System.Globalization.DateTimeStyles]::None, [ref]$startDt)) { break }
}
}
if ($endTime) {
foreach ($fmt in $formats) {
if ([datetime]::TryParseExact($endTime, $fmt, $culture, [System.Globalization.DateTimeStyles]::None, [ref]$endDt)) { break }
}
}
if ($startDt -and $endDt) {
if ($startTime -and $endTime) {
$fmt = 'dd-MM-yyyy HH:mm:ss'
$culture = [System.Globalization.CultureInfo]::InvariantCulture
$startDt = [datetime]::ParseExact($startTime, $fmt, $culture)
$endDt = [datetime]::ParseExact($endTime, $fmt, $culture)
$span = $endDt - $startDt
$durationStr = Format-Duration -Span $span
}
} catch {}
} catch {
Write-Log WARN "[MAIL] Impossibile calcolare la durata: $_"
$durationStr = 'N/D'
}
# --- Dimensioni archivi SQL / FILES, usando in modo sicuro la variabile $moved ---
# --- Dimensioni archivi SQL / FILES ---
$archives = @()
# 1) Prova con la variabile $moved (se il main script la imposta a percorsi)
try {
$movedVar = Get-Variable -Name moved -ErrorAction Stop
$movedVal = $movedVar.Value
@ -216,7 +239,7 @@ $encoded
Write-Log INFO "[MAIL] Variabile 'moved' non disponibile, provo a recuperare i .7z dal log."
}
# Fallback: cerco *.7z dentro il testo del log
# 2) Fallback: cerco *.7z dentro il testo del log (percorso completo)
if (-not $archives -or $archives.Count -eq 0) {
$tokens = $Body -split '\s+'
foreach ($tok in $tokens) {
@ -231,6 +254,51 @@ $encoded
}
}
# 3) Fallback avanzato: usa solo il NOME FILE e cerca in BackupRoot\Files / BackupRoot\Databases / BackupRoot\out
if (-not $archives -or $archives.Count -eq 0) {
try {
$brVar = Get-Variable -Name BackupRoot -ErrorAction SilentlyContinue
if ($brVar -and $brVar.Value) {
$br = $brVar.Value
$dirs = @(
(Join-Path $br 'Files'),
(Join-Path $br 'Databases'),
(Join-Path $br 'out')
)
$tokens = $Body -split '\s+'
$names = @()
foreach ($tok in $tokens) {
if ($tok -like '*.7z') {
$clean = $tok.Trim("`";',.")
if ($clean) {
$name = [System.IO.Path]::GetFileName($clean)
if ($name) { $names += $name }
}
}
}
$names = $names | Select-Object -Unique
foreach ($name in $names) {
foreach ($dir in $dirs) {
if (-not $dir) { continue }
$candidate = Join-Path $dir $name
if (Test-Path -LiteralPath $candidate) {
if (-not ($archives -contains $candidate)) {
$archives += $candidate
}
}
}
}
}
} catch {
Write-Log WARN "[MAIL] Errore nel tentativo di individuare gli archivi in Files/Databases/out: $_"
}
}
[long]$totalBytes = 0
[long]$sqlBytes = 0
[long]$fileBytes = 0
@ -260,9 +328,10 @@ $encoded
$diskUsed = 'N/D'
$diskAvail = 'N/D'
$diskUsePct = 'N/D'
$diskFs = 'N/D'
$diskRoot = $null
try {
$diskRoot = $null
$brVar = Get-Variable -Name BackupRoot -ErrorAction SilentlyContinue
if ($brVar -and $brVar.Value) {
$diskRoot = $brVar.Value
@ -274,6 +343,7 @@ $encoded
$driveRoot = [System.IO.Path]::GetPathRoot($diskRoot)
if ($driveRoot) {
$di = New-Object System.IO.DriveInfo($driveRoot)
$diskFs = $di.DriveFormat
$diskSize = Format-Bytes -Bytes $di.TotalSize
$diskAvail = Format-Bytes -Bytes $di.AvailableFreeSpace
$used = $di.TotalSize - $di.AvailableFreeSpace
@ -287,13 +357,26 @@ $encoded
Write-Log WARN "[MAIL] Impossibile calcolare spazio disco: $_"
}
# Info destinazione (rclone)
# Info destinazione (rclone) + login/dominio + percorso / FS locale
$target = ''
$fst = ''
$login = ''
$domain = ''
$localPathStr = if ($diskRoot) { $diskRoot } else { 'N/D' }
$localFsStr = $diskFs
# Login/Dominio dell'utente Windows che esegue il backup
$login = if ($env:USERNAME) { $env:USERNAME } else { 'N/D' }
$domain = if ($env:USERDOMAIN) { $env:USERDOMAIN }
elseif ($env:COMPUTERNAME) { $env:COMPUTERNAME }
else { 'N/D' }
try {
# 1) Preferisci RcloneRemoteDest (come da backup.conf)
$rdVar = Get-Variable -Name RcloneRemoteDest -ErrorAction SilentlyContinue
if ($rdVar -and $rdVar.Value) {
$target = $rdVar.Value
$fst = 'rclone'
} else {
# 2) Fallback: RcloneRemote/RcloneRemotePath se esistono
$rrVar = Get-Variable -Name RcloneRemote -ErrorAction SilentlyContinue
$rpVar = Get-Variable -Name RcloneRemotePath -ErrorAction SilentlyContinue
$rrVal = if ($rrVar) { $rrVar.Value } else { $null }
@ -307,7 +390,25 @@ $encoded
}
$fst = 'rclone'
}
} catch {}
}
} catch {
Write-Log WARN "[MAIL] Impossibile leggere info rclone: $_"
}
# Arricchisco i campi per la riga "Destinazione backup / File system / tipo"
if ($target -and $localPathStr -and $localFsStr) {
# Esempio:
# Destinazione backup: remote:bucket/path (locale: D:\Backup\out)
# File system / tipo: rclone / NTFS (D:)
$htmlTarget = "$target (locale: $localPathStr)"
if ($diskRoot) {
$driveRoot = [System.IO.Path]::GetPathRoot($diskRoot)
$fst = "$fst / $localFsStr ($driveRoot)"
} else {
$fst = "$fst / $localFsStr"
}
$target = $htmlTarget
}
# Dettagli log in HTML
$details = $Body
@ -338,16 +439,12 @@ $encoded
$html = $html.Replace('XXXWARNINGXXX', $statusInfo.Warning)
$html = $html.Replace('XXXERRORXXX', $statusInfo.Error)
# Dimensioni:
# - Dimensione totale -> totale (SQL + FILES)
# - Dimensione SQL -> solo SQL
# - Dimensione Files -> solo Files
# Dimensioni
$html = $html.Replace('XXXTOTALSIZEXXX', $sizeTotalStr)
$html = $html.Replace('XXXSQLSIZEXXX', $sizeSqlStr)
$html = $html.Replace('XXXFILESSIZEXXX', $sizeFilesStr)
# Destinazione backup
# Destinazione backup + login/dominio + spazio locale
$html = $html.Replace('XXXTARGETXXX', $target)
$html = $html.Replace('XXXFSTXXX', $fst)
$html = $html.Replace('XXXLOGINXXX', $login)