Backup-AdHoc/install-AdHoc-Backup-GUI.ps1

392 lines
22 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Requires -Version 5.1
<#
Installer AdHoc Backup (GUI) - v1.3.4
- Tab "Configurazione (backup.conf)" con UI dinamica (checkbox per booleani, textbox per altri valori)
- Struttura: C:\polo\scripts\bin\RClone\ C:\polo\scripts\bin\7Zip\ C:\polo\scripts\bin\conf\
- Config: C:\polo\scripts\bin\conf\backup.conf e C:\polo\scripts\bin\conf\rclone.conf
- "Installa": scarica/installa Rclone e **solo 7zr.exe** (nessun alias 7z.exe) nelle cartelle bin\
- Dopo estrazione elimina ZIP di Rclone
- Avvio "Configura rclone" con Start-Process diretto (no cmd.exe)
- Schedulazione giornaliera/settimanale via schtasks; AdHoc-Backup.ps1 avviato con -Config <bin\conf\backup.conf>
- Chiusura di tutte le console aperte dallinstaller alla chiusura della finestra
#>
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.IO.Compression.FileSystem
$ErrorActionPreference = 'Stop'
# ------------------------------ HELPERS GRAFICI -------------------------------
function New-Point([object]$x,[object]$y) { try { return New-Object System.Drawing.Point ([int]$x), ([int]$y) } catch { return [System.Drawing.Point]::new([int]$x, [int]$y) } }
function New-Size([object]$w,[object]$h) { try { return New-Object System.Drawing.Size ([int]$w), ([int]$h) } catch { return [System.Drawing.Size]::new([int]$w, [int]$h) } }
function Set-Loc($ctrl,[object]$x,[object]$y){ $ctrl.Location = New-Point $x $y }
function Set-Sz($ctrl,[object]$w,[object]$h){ $ctrl.Size = New-Size $w $h }
# ------------------------------ LOG -------------------------------------------
function New-LogTimestamp { (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') }
$global:LogFile = $null
$global:LogTextBox = $null
function Write-Log {
param([Parameter(Mandatory)][string]$Message,[ValidateSet('INFO','ERR','WARN')] [string]$Level = 'INFO')
$line = "[{0}][{1}] {2}" -f (New-LogTimestamp), $Level, $Message
if ($global:LogTextBox) { try { $global:LogTextBox.AppendText("$line`r`n") } catch {} }
if ($global:LogFile) { try { Add-Content -Path $global:LogFile -Value $line -Encoding UTF8 } catch {} }
Write-Host $line
}
# ------------------------------ UTILS -----------------------------------------
function Ensure-Dir { param([Parameter(Mandatory)][string]$Path)
if (-not (Test-Path -LiteralPath $Path)) { New-Item -ItemType Directory -Path $Path | Out-Null; Write-Log "Cartella creata: $Path" }
else { Write-Log "Cartella già presente: $Path" }
}
function Start-Download {
param([Parameter(Mandatory)][string]$Url,[Parameter(Mandatory)][string]$Dest)
Write-Log "Scarico: $Url"; Write-Log "Destinazione: $Dest"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls
try {
Invoke-WebRequest -Uri $Url -OutFile $Dest -ErrorAction Stop
Write-Log "Download completato."; return $true
} catch {
Write-Log "Invoke-WebRequest fallito: $($_.Exception.Message)" 'WARN'
try { (New-Object System.Net.WebClient).DownloadFile($Url, $Dest); Write-Log "Download completato con WebClient (fallback)."; return $true }
catch { Write-Log "Download fallito: $($_.Exception.Message)" 'ERR'; return $false }
}
}
# Estrattore ZIP robusto (overwrite = true) senza usare overload con Encoding
function Expand-Zip {
param([Parameter(Mandatory)][string]$ZipPath,[Parameter(Mandatory)][string]$DestDir)
Write-Log "Estrazione ZIP: $ZipPath -> $DestDir"
$zip = $null
try {
$zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath)
foreach ($entry in $zip.Entries) {
$targetPath = Join-Path $DestDir $entry.FullName
$targetDir = Split-Path $targetPath -Parent
if ([string]::IsNullOrEmpty($entry.Name)) {
Ensure-Dir $targetPath
} else {
Ensure-Dir $targetDir
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $targetPath, $true)
}
}
} finally {
if ($zip) { $zip.Dispose() }
}
}
function New-DailyOrWeeklyTask {
param([Parameter(Mandatory)][string]$TaskName,
[Parameter(Mandatory)][string]$ScriptPath,
[Parameter(Mandatory)][string]$ConfigPath,
[Parameter(Mandatory)][DateTime]$Time,
[ValidateSet('DAILY','WEEKLY')] [string]$Schedule = 'DAILY',
[ValidateSet('MON','TUE','WED','THU','FRI','SAT','SUN')] [string]$Day = 'MON',
[switch]$Quiet)
$psArgs = @('-NoProfile','-ExecutionPolicy','Bypass','-File',('"{0}"' -f $ScriptPath),'-Config',('"{0}"' -f $ConfigPath))
if ($Quiet) { $psArgs += '-Quiet' }
$tr = 'powershell.exe ' + ($psArgs -join ' ')
$st = $Time.ToString('HH:mm')
$cmd = @('/Create','/SC',$Schedule,'/TN',('"{0}"' -f $TaskName),'/TR',('"{0}"' -f $tr),'/ST',$st,'/RL','HIGHEST','/F')
if ($Schedule -eq 'WEEKLY') { $cmd += @('/D',$Day) }
Write-Log "Creo/aggiorno attività pianificata: $TaskName ($Schedule) alle $st"
try { schtasks.exe @cmd | Out-Null; Write-Log "Attività pianificata impostata."; return $true }
catch { Write-Log "Creazione attività fallita: $($_.Exception.Message)" 'ERR'; return $false }
}
# ------------------------------ LAYOUT BIN/CONF -------------------------------
function Ensure-ToolsLayout {
param([Parameter(Mandatory)][string]$Root)
$binRoot = Join-Path $Root 'bin'
$confRoot = Join-Path $binRoot 'conf'
$rcloneDir= Join-Path $binRoot 'RClone'
$sevenDir = Join-Path $binRoot '7Zip'
Ensure-Dir $binRoot
Ensure-Dir $confRoot
Ensure-Dir $rcloneDir
Ensure-Dir $sevenDir
return @{ Bin=$binRoot; Conf=$confRoot; RClone=$rcloneDir; SevenZip=$sevenDir }
}
function Ensure-RcloneExe {
param([Parameter(Mandatory)][string]$RcloneDir)
$exe = Join-Path $RcloneDir 'rclone.exe'
if (-not (Test-Path -LiteralPath $exe)) {
$zip = Join-Path $RcloneDir 'rclone-current-windows-amd64.zip'
if (Start-Download -Url 'https://downloads.rclone.org/rclone-current-windows-amd64.zip' -Dest $zip) {
Expand-Zip -ZipPath $zip -DestDir $RcloneDir
$found = Get-ChildItem -LiteralPath $RcloneDir -Recurse -Filter 'rclone.exe' | Select-Object -First 1
if ($found) { Copy-Item -LiteralPath $found.FullName -Destination $exe -Force }
Get-ChildItem -LiteralPath $RcloneDir -Directory | Where-Object { $_.Name -like 'rclone-*' } | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $zip -Force -ErrorAction SilentlyContinue
}
}
return $exe
}
# === SOLO 7zr.exe =============================================================
function Ensure-SevenZipExe {
param([Parameter(Mandatory)][string]$SevenZipDir)
$exe = Join-Path $SevenZipDir '7zr.exe'
if (-not (Test-Path -LiteralPath $exe)) {
$url7zr = 'https://www.7-zip.org/a/7zr.exe'
if (Start-Download -Url $url7zr -Dest $exe) {
Write-Log "Installato 7-Zip minimal: $exe"
} else {
Write-Log "Impossibile installare 7-Zip (7zr.exe)." 'ERR'
}
}
return $exe
}
# ------------------------------ CONF PARSER -----------------------------------
$global:ChildPids = New-Object System.Collections.ArrayList
function Track-Child($p){ if ($p -and $p.Id) { [void]$global:ChildPids.Add($p.Id) } }
$global:ConfUi = @{
Map = $null
Order = @()
Controls = @{}
SourceLines = @()
TempPath = Join-Path $env:TEMP 'adhoc_backup_conf_preview.conf'
}
function Is-BoolString([string]$s){ if ($null -eq $s) { return $false }
switch ($s.Trim().ToLowerInvariant()) { 'true' {1};'false' {1};'1' {1};'0' {1};'yes' {1};'no' {1};'y' {1};'n' {1}; default {0} } }
function Parse-BackupConf {
param([Parameter(Mandatory)][string]$Path)
$order = New-Object System.Collections.ArrayList
$map = [ordered]@{}
$lines = Get-Content -LiteralPath $Path -ErrorAction Stop
foreach ($line in $lines) {
$trim = $line.Trim()
if ($trim -eq '' -or $trim.StartsWith('#') -or $trim.StartsWith(';')) { [void]$order.Add(@{type='comment';text=$line}); continue }
$idx = $trim.IndexOf('='); if ($idx -lt 1) { [void]$order.Add(@{type='raw';text=$line}); continue }
$k = $trim.Substring(0,$idx).Trim(); $v = $trim.Substring($idx+1).Trim()
$v = ($v -replace '(\s+)[#;].*$', '').Trim()
if (($v.StartsWith('"') -and $v.EndsWith('"')) -or ($v.StartsWith("'") -and $v.EndsWith("'"))) { $v = $v.Substring(1,$v.Length-2) }
$map[$k] = $v; [void]$order.Add(@{type='kv';key=$k})
}
$global:ConfUi.Map=$map; $global:ConfUi.Order=$order; $global:ConfUi.SourceLines=$lines
}
function Save-BackupConf { param([Parameter(Mandatory)][string]$Path)
$out = New-Object System.Collections.Generic.List[string]
foreach ($entry in $global:ConfUi.Order){
if ($entry.type -eq 'kv'){
$k=$entry.key; $v=$global:ConfUi.Map[$k]
if (Is-BoolString $v) { $v = ([string]$v).Trim().ToLowerInvariant(); if ($v -in @('1','yes','y')){$v='true'} elseif ($v -in @('0','no','n')){$v='false'} }
$out.Add("$k=$v")
} else { $out.Add($entry.text) }
}
foreach ($k in $global:ConfUi.Map.Keys){
if (-not ($global:ConfUi.Order | Where-Object { $_.type -eq 'kv' -and $_.key -eq $k })) { $out.Add("$k=$($global:ConfUi.Map[$k])") }
}
Set-Content -LiteralPath $Path -Value $out -Encoding UTF8
}
# ------------------------------ DEFAULTS --------------------------------------
$defaultInstall = 'C:\polo\scripts'
$defaultScriptUrl = 'https://gitea.poloinformatico.it/Mattia/Backup-AdHoc/raw/branch/main/AdHoc-Backup.ps1'
$defaultConfUrl = 'https://gitea.poloinformatico.it/Mattia/Backup-AdHoc/raw/branch/main/backup.conf'
# ------------------------------ UI --------------------------------------------
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Installer AdHoc Backup'
$form.StartPosition = 'CenterScreen'
$form.MaximizeBox = $true
$form.FormBorderStyle = 'FixedDialog'
Set-Sz $form 900 860
# Install path
$lblInstall = New-Object System.Windows.Forms.Label
$lblInstall.Text = 'Cartella di installazione'; Set-Loc $lblInstall 10 12; Set-Sz $lblInstall 180 20; $form.Controls.Add($lblInstall)
$txtInstall = New-Object System.Windows.Forms.TextBox
Set-Loc $txtInstall 10 32; Set-Sz $txtInstall 760 22; $txtInstall.Text = $defaultInstall; $form.Controls.Add($txtInstall)
$btnBrowse = New-Object System.Windows.Forms.Button
$btnBrowse.Text = 'Scegli...'; Set-Loc $btnBrowse 780 30; Set-Sz $btnBrowse 90 26; $form.Controls.Add($btnBrowse)
# URLs
$lblScriptUrl = New-Object System.Windows.Forms.Label; $lblScriptUrl.Text = 'Script URL'; Set-Loc $lblScriptUrl 10 64; Set-Sz $lblScriptUrl 150 20; $form.Controls.Add($lblScriptUrl)
$txtScriptUrl = New-Object System.Windows.Forms.TextBox; Set-Loc $txtScriptUrl 10 84; Set-Sz $txtScriptUrl 860 22; $txtScriptUrl.Text = $defaultScriptUrl; $form.Controls.Add($txtScriptUrl)
$lblConfUrl = New-Object System.Windows.Forms.Label; $lblConfUrl.Text = 'Config URL'; Set-Loc $lblConfUrl 10 116; Set-Sz $lblConfUrl 150 20; $form.Controls.Add($lblConfUrl)
$txtConfUrl = New-Object System.Windows.Forms.TextBox; Set-Loc $txtConfUrl 10 136; Set-Sz $txtConfUrl 860 22; $txtConfUrl.Text = $defaultConfUrl; $form.Controls.Add($txtConfUrl)
# Opzioni
$grpOptions = New-Object System.Windows.Forms.GroupBox; $grpOptions.Text = 'Opzioni'; Set-Loc $grpOptions 10 170; Set-Sz $grpOptions 340 120; $form.Controls.Add($grpOptions)
$chkForceConf = New-Object System.Windows.Forms.CheckBox; $chkForceConf.Text='Sovrascrivi config esistente (ForceConfig)'; Set-Loc $chkForceConf 10 20; Set-Sz $chkForceConf 300 22; $grpOptions.Controls.Add($chkForceConf)
$chkNoRun = New-Object System.Windows.Forms.CheckBox; $chkNoRun.Text='Non avviare il backup ora (NoRun)'; Set-Loc $chkNoRun 10 45; Set-Sz $chkNoRun 280 22; $grpOptions.Controls.Add($chkNoRun)
$chkQuiet = New-Object System.Windows.Forms.CheckBox; $chkQuiet.Text='Modalità silenziosa (Quiet)'; Set-Loc $chkQuiet 10 70; Set-Sz $chkQuiet 220 22; $grpOptions.Controls.Add($chkQuiet)
# Schedulazione
$grpSched = New-Object System.Windows.Forms.GroupBox; $grpSched.Text='Schedulazione'; Set-Loc $grpSched 360 170; Set-Sz $grpSched 510 160; $form.Controls.Add($grpSched)
$chkSchedule = New-Object System.Windows.Forms.CheckBox; $chkSchedule.Text='Crea attività pianificata'; Set-Loc $chkSchedule 10 20; Set-Sz $chkSchedule 200 22; $chkSchedule.Checked=$true; $grpSched.Controls.Add($chkSchedule)
$lblFreq = New-Object System.Windows.Forms.Label; $lblFreq.Text='Frequenza:'; Set-Loc $lblFreq 20 50; Set-Sz $lblFreq 80 22; $grpSched.Controls.Add($lblFreq)
$cmbFreq = New-Object System.Windows.Forms.ComboBox; $cmbFreq.DropDownStyle='DropDownList'; [void]$cmbFreq.Items.Add('Giornaliera'); [void]$cmbFreq.Items.Add('Settimanale'); $cmbFreq.SelectedIndex=0; Set-Loc $cmbFreq 100 48; Set-Sz $cmbFreq 120 22; $grpSched.Controls.Add($cmbFreq)
$lblDOW = New-Object System.Windows.Forms.Label; $lblDOW.Text='Giorno:'; Set-Loc $lblDOW 240 50; Set-Sz $lblDOW 60 22; $grpSched.Controls.Add($lblDOW)
$cmbDOW = New-Object System.Windows.Forms.ComboBox; $cmbDOW.DropDownStyle='DropDownList'
'Lunedì (MON)','Martedì (TUE)','Mercoledì (WED)','Giovedì (THU)','Venerdì (FRI)','Sabato (SAT)','Domenica (SUN)' | ForEach-Object { [void]$cmbDOW.Items.Add($_) }
$cmbDOW.SelectedIndex=0; Set-Loc $cmbDOW 300 48; Set-Sz $cmbDOW 180 22; $cmbDOW.Enabled=$false; $grpSched.Controls.Add($cmbDOW)
$cmbFreq.Add_SelectedIndexChanged({ $cmbDOW.Enabled = ($cmbFreq.SelectedIndex -eq 1) })
$lblTime = New-Object System.Windows.Forms.Label; $lblTime.Text='Ora:'; Set-Loc $lblTime 20 85; Set-Sz $lblTime 40 22; $grpSched.Controls.Add($lblTime)
$timePicker = New-Object System.Windows.Forms.DateTimePicker; $timePicker.Format=[System.Windows.Forms.DateTimePickerFormat]::Time; $timePicker.ShowUpDown=$true
$tpDefault = Get-Date; $tpDefault = Get-Date -Hour 23 -Minute 0 -Second 0
$timePicker.Value = $tpDefault; Set-Loc $timePicker 60 82; Set-Sz $timePicker 100 22; $grpSched.Controls.Add($timePicker)
$lblTask = New-Object System.Windows.Forms.Label; $lblTask.Text='Nome task:'; Set-Loc $lblTask 180 85; Set-Sz $lblTask 80 22; $grpSched.Controls.Add($lblTask)
$txtTask = New-Object System.Windows.Forms.TextBox; Set-Loc $txtTask 260 82; Set-Sz $txtTask 230 22; $txtTask.Text='Backup_AdHoc'; $grpSched.Controls.Add($txtTask)
# Argomenti extra
$lblExtra = New-Object System.Windows.Forms.Label; $lblExtra.Text='Argomenti extra per AdHoc (es. -WhatIf)'; Set-Loc $lblExtra 10 340; Set-Sz $lblExtra 380 20; $form.Controls.Add($lblExtra)
$txtExtra = New-Object System.Windows.Forms.TextBox; Set-Loc $txtExtra 10 362; Set-Sz $txtExtra 860 22; $form.Controls.Add($txtExtra)
# ------------------------------ TAB: CONFIGURAZIONE ---------------------------
$tabs = New-Object System.Windows.Forms.TabControl; Set-Loc $tabs 10 392; Set-Sz $tabs 860 230; $form.Controls.Add($tabs)
$tabConfig = New-Object System.Windows.Forms.TabPage; $tabConfig.Text='Configurazione (backup.conf)'; [void]$tabs.TabPages.Add($tabConfig)
$cfgPanel = New-Object System.Windows.Forms.Panel; Set-Loc $cfgPanel 10 10; Set-Sz $cfgPanel 830 150; $cfgPanel.AutoScroll=$true; $tabConfig.Controls.Add($cfgPanel)
$btnReloadConf = New-Object System.Windows.Forms.Button; $btnReloadConf.Text='Ricarica da URL'; Set-Loc $btnReloadConf 10 170; Set-Sz $btnReloadConf 120 26; $tabConfig.Controls.Add($btnReloadConf)
$btnSaveConf = New-Object System.Windows.Forms.Button; $btnSaveConf.Text='Salva in bin\\conf'; Set-Loc $btnSaveConf 140 170; Set-Sz $btnSaveConf 230 26; $tabConfig.Controls.Add($btnSaveConf)
$btnRcloneConfig = New-Object System.Windows.Forms.Button; $btnRcloneConfig.Text='Configura rclone (console)'; Set-Loc $btnRcloneConfig 380 170; Set-Sz $btnRcloneConfig 170 26; $tabConfig.Controls.Add($btnRcloneConfig)
function Build-ConfigUI {
$cfgPanel.Controls.Clear(); $global:ConfUi.Controls = @{}
$xLabel=10; $xCtrl=260; $y=10; $h=24
foreach ($k in $global:ConfUi.Map.Keys) {
$v = $global:ConfUi.Map[$k]
$lbl = New-Object System.Windows.Forms.Label; $lbl.Text=$k; Set-Loc $lbl $xLabel ($y+4); Set-Sz $lbl 240 $h; $cfgPanel.Controls.Add($lbl)
if (Is-BoolString $v) {
$chk = New-Object System.Windows.Forms.CheckBox
$chk.Checked = (('1','true','yes','y') -contains ($v.ToString().ToLower()))
Set-Loc $chk $xCtrl ($y+2); Set-Sz $chk 20 $h
$chk.Add_CheckedChanged({ $global:ConfUi.Map[$k] = if ($chk.Checked) { 'true' } else { 'false' } })
$cfgPanel.Controls.Add($chk); $global:ConfUi.Controls[$k]=$chk
} else {
$tb = New-Object System.Windows.Forms.TextBox; $tb.Text=$v; Set-Loc $tb $xCtrl $y; Set-Sz $tb 540 $h
$tb.Add_TextChanged({ $global:ConfUi.Map[$k] = $tb.Text })
$cfgPanel.Controls.Add($tb); $global:ConfUi.Controls[$k]=$tb
}
$y += 28
}
}
$btnReloadConf.Add_Click({
try {
$temp = $global:ConfUi.TempPath
if (Start-Download -Url $txtConfUrl.Text -Dest $temp) {
Parse-BackupConf -Path $temp; Build-ConfigUI; Write-Log "backup.conf scaricato e caricato in editor: $temp"
}
} catch { Write-Log "Errore ricarica backup.conf: $($_.Exception.Message)" 'ERR' }
})
$btnSaveConf.Add_Click({
try {
$layout = Ensure-ToolsLayout -Root $txtInstall.Text
$dest = Join-Path $layout.Conf 'backup.conf'
Save-BackupConf -Path $dest
Write-Log "backup.conf salvato in: $dest"
} catch { Write-Log "Errore salvataggio backup.conf: $($_.Exception.Message)" 'ERR' }
})
# Avvio rclone config in console dedicata
$btnRcloneConfig.Add_Click({
try {
$layout = Ensure-ToolsLayout -Root $txtInstall.Text
$rcloneExe = Ensure-RcloneExe -RcloneDir $layout.RClone
$rcloneConf = Join-Path $layout.Conf 'rclone.conf'
$p = Start-Process -FilePath $rcloneExe -ArgumentList @('config','--config', $rcloneConf) -WorkingDirectory $layout.RClone -PassThru
Track-Child $p
Write-Log "Avviato rclone config (PID=$($p.Id))"
} catch { Write-Log "Errore avvio rclone config: $($_.Exception.Message)" 'ERR' }
})
# ------------------------------ BOTTONI AZIONE --------------------------------
$btnInstall = New-Object System.Windows.Forms.Button; $btnInstall.Text='Installa'; Set-Loc $btnInstall 10 652; Set-Sz $btnInstall 120 30; $form.Controls.Add($btnInstall)
$btnClose = New-Object System.Windows.Forms.Button; $btnClose.Text='Chiudi'; Set-Loc $btnClose 150 652; Set-Sz $btnClose 120 30; $form.Controls.Add($btnClose)
# LOG
$lblLog = New-Object System.Windows.Forms.Label; $lblLog.Text='Log'; Set-Loc $lblLog 10 692; Set-Sz $lblLog 50 20; $form.Controls.Add($lblLog)
$txtLog = New-Object System.Windows.Forms.TextBox; Set-Loc $txtLog 10 712; Set-Sz $txtLog 860 100; $txtLog.Multiline=$true; $txtLog.ScrollBars='Vertical'; $txtLog.ReadOnly=$true; $form.Controls.Add($txtLog); $global:LogTextBox = $txtLog
# ------------------------------ HANDLERS --------------------------------------
$btnBrowse.Add_Click({ $dlg = New-Object System.Windows.Forms.FolderBrowserDialog; $dlg.Description='Seleziona cartella di installazione'; if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){ $txtInstall.Text=$dlg.SelectedPath } })
$btnInstall.Add_Click({
try {
$btnInstall.Enabled = $false
$root = $txtInstall.Text; Ensure-Dir $root
$layout = Ensure-ToolsLayout -Root $root
# Scarica/installa Rclone e SOLO 7zr.exe
$rcloneExe = Ensure-RcloneExe -RcloneDir $layout.RClone
if (-not (Test-Path -LiteralPath $rcloneExe)) { Write-Log "ATTENZIONE: rclone.exe non installato." 'WARN' }
$sevenExe = Ensure-SevenZipExe -SevenZipDir $layout.SevenZip
if (-not (Test-Path -LiteralPath $sevenExe)) { Write-Log "ATTENZIONE: 7-Zip (7zr.exe) non installato." 'WARN' }
# Script principale
$scriptPath = Join-Path $root 'AdHoc-Backup.ps1'
if (-not (Start-Download -Url $txtScriptUrl.Text -Dest $scriptPath)) { throw "Impossibile scaricare lo script principale." }
# backup.conf -> bin\conf\backup.conf
$confPath = Join-Path $layout.Conf 'backup.conf'
if ($global:ConfUi.Map) { Save-BackupConf -Path $confPath }
else {
if (-not (Start-Download -Url $txtConfUrl.Text -Dest $confPath)) { Write-Log "Attenzione: impossibile ottenere backup.conf; procedo senza." 'WARN' }
}
# Schedulazione
if ($chkSchedule.Checked) {
$schedule = if ($cmbFreq.SelectedIndex -eq 1) { 'WEEKLY' } else { 'DAILY' }
$dowmap = @('MON','TUE','WED','THU','FRI','SAT','SUN'); $day=$dowmap[0]; if ($schedule -eq 'WEEKLY'){ $day=$dowmap[$cmbDOW.SelectedIndex] }
[void](New-DailyOrWeeklyTask -TaskName $txtTask.Text -ScriptPath $scriptPath -ConfigPath $confPath -Time $timePicker.Value -Schedule $schedule -Day $day -Quiet:$chkQuiet.Checked)
}
# Avvio immediato
if (-not $chkNoRun.Checked) {
$args=@('-NoProfile','-ExecutionPolicy','Bypass','-File',"`"$scriptPath`"","-Config", "`"$confPath`"")
if ($chkQuiet.Checked){ $args += '-Quiet' }
if ($txtExtra.Text -and $txtExtra.Text.Trim() -ne ''){ $args += $txtExtra.Text.Trim().Split(' ') }
$p=Start-Process -FilePath 'powershell.exe' -ArgumentList $args -PassThru; Track-Child $p; Write-Log "Esecuzione avviata (PID=$($p.Id))."
}
Write-Log "Installazione completata."
} catch { Write-Log "Errore durante l'installazione: $($_.Exception.Message)" 'ERR' }
finally { $btnInstall.Enabled = $true }
})
$btnClose.Add_Click({ $form.Close() })
# Evita conflitto con variabile automatica $PID
$form.Add_FormClosed({
foreach ($cpid in $global:ChildPids) {
try {
$proc = Get-Process -Id $cpid -ErrorAction SilentlyContinue
if ($proc) {
$null = $proc.CloseMainWindow()
Start-Sleep -Milliseconds 200
if (-not $proc.HasExited) { Stop-Process -Id $cpid -Force -ErrorAction SilentlyContinue }
}
} catch {}
}
})
# ------------------------------ AVVIO -----------------------------------------
$global:LogFile = Join-Path $env:TEMP "install-adhoc-backup_gui_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
Write-Log "Log file: $global:LogFile"
try {
$temp = $global:ConfUi.TempPath
if (Start-Download -Url $defaultConfUrl -Dest $temp) { Parse-BackupConf -Path $temp; Build-ConfigUI; Write-Log "backup.conf iniziale caricato da: $defaultConfUrl" }
else { Write-Log "Non sono riuscito a scaricare il backup.conf iniziale." 'WARN' }
} catch { Write-Log "Errore iniziale confer: $($_.Exception.Message)" 'ERR' }
[void]$form.ShowDialog()