diff --git a/send-mail.ps1 b/send-mail.ps1
index a6c7783..64a8354 100644
--- a/send-mail.ps1
+++ b/send-mail.ps1
@@ -52,21 +52,21 @@ function Get-StatusInfo {
# Default: COMPLETATO (verde)
$statusText = 'COMPLETATO'
- $color = '#4CAF50' # verde
+ $color = '#4CAF50' # verde
$succ = '1'
$warn = '0'
$err = '0'
if ($Body -match 'ESITO:\s*FALLITO') {
$statusText = 'FALLITO'
- $color = '#F44336' # rosso
+ $color = '#F44336' # rosso
$succ = '0'
$warn = '0'
$err = '1'
}
elseif ($Body -match 'ESITO:\s*COMPLETATO CON WARNING') {
$statusText = 'COMPLETATO CON WARNING'
- $color = '#FFC107' # giallo
+ $color = '#FFC107' # giallo
$succ = '0'
$warn = '1'
$err = '0'
@@ -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 '&','&' -replace '<','<' -replace '>','>'
$encoded = $encoded -replace "`r`n","
" -replace "`n","
"
+
@"
-
$Subject
+
+
+ $Subject
+
$encoded
@@ -143,10 +147,14 @@ $encoded
if (-not $template) {
$encoded = $Body -replace '&','&' -replace '<','<' -replace '>','>'
$encoded = $encoded -replace "`r`n","
" -replace "`n","
"
+
@"
-$Subject
+
+
+ $Subject
+
$encoded
@@ -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 l’istante 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 l’ora 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) {
- $span = $endDt - $startDt
+ 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,13 +254,58 @@ $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
foreach ($p in $archives) {
try {
- $fi = Get-Item -LiteralPath $p -ErrorAction Stop
+ $fi = Get-Item -LiteralPath $p -ErrorAction Stop
$size = [long]$fi.Length
$totalBytes += $size
@@ -252,17 +320,18 @@ $encoded
}
$sizeTotalStr = if ($totalBytes -gt 0) { Format-Bytes -Bytes $totalBytes } else { 'N/D' }
- $sizeSqlStr = if ($sqlBytes -gt 0) { Format-Bytes -Bytes $sqlBytes } else { 'N/D' }
- $sizeFilesStr = if ($fileBytes -gt 0) { Format-Bytes -Bytes $fileBytes } else { 'N/D' }
+ $sizeSqlStr = if ($sqlBytes -gt 0) { Format-Bytes -Bytes $sqlBytes } else { 'N/D' }
+ $sizeFilesStr = if ($fileBytes -gt 0) { Format-Bytes -Bytes $fileBytes } else { 'N/D' }
# Info disco (unità di BackupRoot o del primo archivio)
$diskSize = 'N/D'
$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
@@ -273,7 +342,8 @@ $encoded
if ($diskRoot) {
$driveRoot = [System.IO.Path]::GetPathRoot($diskRoot)
if ($driveRoot) {
- $di = New-Object System.IO.DriveInfo($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,27 +357,58 @@ $encoded
Write-Log WARN "[MAIL] Impossibile calcolare spazio disco: $_"
}
- # Info destinazione (rclone)
- $target = ''
- $fst = ''
- $login = ''
- $domain = ''
+ # Info destinazione (rclone) + login/dominio + percorso / FS locale
+ $target = ''
+ $fst = ''
+ $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 {
- $rrVar = Get-Variable -Name RcloneRemote -ErrorAction SilentlyContinue
- $rpVar = Get-Variable -Name RcloneRemotePath -ErrorAction SilentlyContinue
- $rrVal = if ($rrVar) { $rrVar.Value } else { $null }
- $rpVal = if ($rpVar) { $rpVar.Value } else { $null }
+ # 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 }
+ $rpVal = if ($rpVar) { $rpVar.Value } else { $null }
- if ($rrVal -or $rpVal) {
- if ($rpVal) {
- $target = "$rrVal`:$rpVal"
- } else {
- $target = "$rrVal`:"
+ if ($rrVal -or $rpVal) {
+ if ($rpVal) {
+ $target = "$rrVal`:$rpVal"
+ } else {
+ $target = "$rrVal`:"
+ }
+ $fst = 'rclone'
}
- $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
@@ -323,9 +424,9 @@ $encoded
$html = $html.Replace('XXXSTATUSXXX', $statusInfo.Text)
# Info generali
- $html = $html.Replace('XXXAGENTXXX', $agent)
- $html = $html.Replace('XXXHOSTNAMEXXX', $agent)
- $html = $html.Replace('XXXVERSIONXXX', $scriptVersion)
+ $html = $html.Replace('XXXAGENTXXX', $agent)
+ $html = $html.Replace('XXXHOSTNAMEXXX', $agent)
+ $html = $html.Replace('XXXVERSIONXXX', $scriptVersion)
# Date / orari
$html = $html.Replace('XXXBACKUPDATETIMEXXX', $backupDateTime)
@@ -338,26 +439,22 @@ $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
-
- $html = $html.Replace('XXXTOTALSIZEXXX', $sizeTotalStr)
- $html = $html.Replace('XXXSQLSIZEXXX', $sizeSqlStr)
+ # 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)
$html = $html.Replace('XXXDOMAINXXX', $domain)
# Info disco
- $html = $html.Replace('XXXDISKSIZEXXX', $diskSize)
- $html = $html.Replace('XXXDISKUSEDXXX', $diskUsed)
- $html = $html.Replace('XXXDISKAVAILXXX', $diskAvail)
- $html = $html.Replace('XXXDISKUSEPXXX', $diskUsePct)
+ $html = $html.Replace('XXXDISKSIZEXXX', $diskSize)
+ $html = $html.Replace('XXXDISKUSEDXXX', $diskUsed)
+ $html = $html.Replace('XXXDISKAVAILXXX', $diskAvail)
+ $html = $html.Replace('XXXDISKUSEPXXX', $diskUsePct)
# Dettaglio log
$html = $html.Replace('XXXDETAILSXXX', $details)
@@ -382,11 +479,11 @@ function Send-ReportMail {
try {
$shVar = Get-Variable -Name MailSmtpHost -ErrorAction Stop
$spVar = Get-Variable -Name MailSmtpPort -ErrorAction Stop
- $maVar = Get-Variable -Name MailUseAuth -ErrorAction SilentlyContinue
- $muVar = Get-Variable -Name MailUser -ErrorAction SilentlyContinue
- $mpVar = Get-Variable -Name MailPassword -ErrorAction SilentlyContinue
- $mfVar = Get-Variable -Name MailFrom -ErrorAction Stop
- $mtVar = Get-Variable -Name MailTo -ErrorAction Stop
+ $maVar = Get-Variable -Name MailUseAuth -ErrorAction SilentlyContinue
+ $muVar = Get-Variable -Name MailUser -ErrorAction SilentlyContinue
+ $mpVar = Get-Variable -Name MailPassword -ErrorAction SilentlyContinue
+ $mfVar = Get-Variable -Name MailFrom -ErrorAction Stop
+ $mtVar = Get-Variable -Name MailTo -ErrorAction Stop
$MailSmtpHost = $shVar.Value
$MailSmtpPort = $spVar.Value
@@ -397,16 +494,16 @@ function Send-ReportMail {
$MailTo = $mtVar.Value
$smtp = New-Object System.Net.Mail.SmtpClient($MailSmtpHost, $MailSmtpPort)
- $smtp.EnableSsl = $true
- $smtp.DeliveryMethod = [System.Net.Mail.SmtpDeliveryMethod]::Network
+ $smtp.EnableSsl = $true
+ $smtp.DeliveryMethod = [System.Net.Mail.SmtpDeliveryMethod]::Network
$smtp.UseDefaultCredentials = $false
if ($MailUseAuth -and $MailUser -and $MailPassword) {
$smtp.Credentials = New-Object System.Net.NetworkCredential($MailUser, $MailPassword)
}
- $msg = New-Object System.Net.Mail.MailMessage
- $msg.From = $MailFrom
+ $msg = New-Object System.Net.Mail.MailMessage
+ $msg.From = $MailFrom
foreach ($rcpt in @($MailTo)) {
if ($rcpt) { $msg.To.Add($rcpt) }
@@ -425,4 +522,4 @@ function Send-ReportMail {
}
}
-Send-ReportMail -Subject $Subject -Body $Body
+Send-ReportMail -Subject $Subject -Body $Body
\ No newline at end of file