From 31292e159c8e5724994eb9c100843c323d0071b1 Mon Sep 17 00:00:00 2001 From: devops Date: Thu, 5 Mar 2026 08:58:52 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20MailBoxBackup=5FTB.ps1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MailBoxBackup_TB.ps1 | 894 +++++++++++++++++++++---------------------- 1 file changed, 447 insertions(+), 447 deletions(-) diff --git a/MailBoxBackup_TB.ps1 b/MailBoxBackup_TB.ps1 index f798749..e921d11 100644 --- a/MailBoxBackup_TB.ps1 +++ b/MailBoxBackup_TB.ps1 @@ -1,448 +1,448 @@ -<# -.SYNOPSIS - Скрипт для закрытия Thunderbird и резервного копирования профиля -.DESCRIPTION - Выполняет поиск процессов Thunderbird, завершает их, ожидает полного закрытия - и создает копию папки профиля в указанное место. Автоматически создает папки назначения. -.NOTES - Author: - Date: $(Get-Date -Format 'dd.MM.yyyy') -#> - -# === НАСТРОЙКИ === -$ThunderbirdProcessName = "thunderbird" # Имя процесса без .exe -$SourceProfilePath = "$env:APPDATA\Thunderbird\Profiles" # Стандартный путь к профилям - -# Настройка пути для резервной копии (папка создастся автоматически) -$BackupRoot = "E:\Backups\Thunderbird" # Корневая папка для бэкапов -$BackupDestination = Join-Path -Path $BackupRoot -ChildPath (Get-Date -Format 'yyyy-MM-dd') # Папка с датой - -$MaxWaitTimeSeconds = 30 # Максимальное время ожидания закрытия Thunderbird (в секундах) -$CopyRetryCount = 3 # Количество попыток копирования при ошибках -$MinFreeSpaceMB = 500 # Минимальное свободное место на диске назначения (МБ) -# ================= - -# Функция для записи лога -function Write-Log { - param( - [string]$Message, - [string]$Type = "INFO" - ) - $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - - # Цвета для разных типов сообщений - switch ($Type) { - "SUCCESS" { $color = "Green" } - "ERROR" { $color = "Red" } - "WARNING" { $color = "Yellow" } - "INFO" { $color = "Cyan" } - default { $color = "White" } - } - - Write-Host "[$timestamp] [$Type] " -NoNewline - Write-Host "$Message" -ForegroundColor $color -} - -# Функция для создания папки назначения -function New-BackupFolder { - param( - [string]$FolderPath - ) - - try { - if (-not (Test-Path $FolderPath)) { - Write-Log "Папка назначения не существует. Создание: $FolderPath" -Type "INFO" - - # Создаем все папки в пути рекурсивно - New-Item -Path $FolderPath -ItemType Directory -Force | Out-Null - - Write-Log "Папка успешно создана" -Type "SUCCESS" - } else { - Write-Log "Папка назначения уже существует: $FolderPath" -Type "INFO" - } - - return $true - } catch { - Write-Log "ОШИБКА при создании папки назначения: $_" -Type "ERROR" - return $false - } -} - -# Функция для проверки свободного места на диске -function Test-FreeSpace { - param( - [string]$Path, - [long]$RequiredSpaceMB = 500 - ) - - try { - # Получаем корневой диск из пути - $rootPath = Split-Path -Path $Path -Qualifier - if ([string]::IsNullOrEmpty($rootPath)) { - $rootPath = $Path[0] + ":" - } - - $drive = Get-PSDrive -Name $rootPath[0] -ErrorAction Stop - $freeSpaceMB = [math]::Round($drive.Free / 1MB, 2) - - Write-Log "Свободное место на диске $rootPath : $freeSpaceMB MB" -Type "INFO" - - if ($drive.Free -lt ($RequiredSpaceMB * 1MB)) { - Write-Log "НЕДОСТАТОЧНО МЕСТА! Требуется: $RequiredSpaceMB MB, доступно: $freeSpaceMB MB" -Type "ERROR" - return $false - } - - Write-Log "Места достаточно: $freeSpaceMB MB свободно" -Type "SUCCESS" - return $true - } catch { - Write-Log "Не удалось проверить свободное место: $_" -Type "WARNING" - return $true # Продолжаем, если не удалось проверить - } -} - -# Функция для проверки, запущен ли Thunderbird -function Test-ThunderbirdRunning { - $processes = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue - return ($processes.Count -gt 0) -} - -# Функция для закрытия Thunderbird -function Stop-ThunderbirdGracefully { - Write-Log "Поиск запущенных процессов Thunderbird..." -Type "INFO" - - if (Test-ThunderbirdRunning) { - $processCount = (Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue).Count - Write-Log "Найдено процессов Thunderbird: $processCount. Попытка корректного завершения..." -Type "WARNING" - - # Пытаемся закрыть окна Thunderbird (более мягкий способ) - $thunderbirdWindows = Get-Process -Name $ThunderbirdProcessName | Where-Object { $_.MainWindowTitle -ne "" } - - if ($thunderbirdWindows) { - foreach ($proc in $thunderbirdWindows) { - $windowTitle = if ($proc.MainWindowTitle) { $proc.MainWindowTitle } else { "Без заголовка" } - Write-Log "Закрытие окна: $windowTitle" - $proc.CloseMainWindow() | Out-Null - } - } else { - Write-Log "Нет видимых окон Thunderbird, но процессы запущены" -Type "WARNING" - } - - # Даем время на закрытие - $waitCounter = 0 - Write-Host "" - while ((Test-ThunderbirdRunning) -and ($waitCounter -lt $MaxWaitTimeSeconds)) { - Start-Sleep -Seconds 1 - $waitCounter++ - - # Простой индикатор прогресса - $progress = [math]::Round(($waitCounter / $MaxWaitTimeSeconds) * 100) - Write-Host "`rОжидание закрытия Thunderbird: $progress% " -NoNewline - } - Write-Host "" - - # Если всё ещё запущен - принудительно завершаем - if (Test-ThunderbirdRunning) { - Write-Log "Thunderbird не закрылся за отведенное время. Принудительное завершение..." -Type "ERROR" - - $remainingProcesses = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue - foreach ($proc in $remainingProcesses) { - try { - Stop-Process -Id $proc.Id -Force - Write-Log "Процесс PID $($proc.Id) принудительно завершен" -Type "WARNING" - } catch { - Write-Log "Не удалось завершить процесс PID $($proc.Id): $_" -Type "ERROR" - } - } - - Start-Sleep -Seconds 3 - } else { - Write-Log "Thunderbird успешно закрыт" -Type "SUCCESS" - } - } else { - Write-Log "Thunderbird не запущен" -Type "INFO" - } - - # Финальная проверка - if (Test-ThunderbirdRunning) { - Write-Log "ВНИМАНИЕ: Некоторые процессы Thunderbird всё еще запущены!" -Type "ERROR" - - # Показываем оставшиеся процессы - Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue | - ForEach-Object { Write-Log " - PID: $($_.Id), Запущен: $($_.StartTime)" -Type "ERROR" } - - Write-Log "Продолжаем копирование, но некоторые файлы могут быть заблокированы" -Type "WARNING" - return $false - } - - return $true -} - -# Функция для получения размера папки -function Get-FolderSize { - param([string]$Path) - - try { - if (Test-Path $Path) { - $size = (Get-ChildItem $Path -Recurse -ErrorAction SilentlyContinue | - Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum - if ($size) { - return [math]::Round($size / 1MB, 2) - } - } - } catch { - # Игнорируем ошибки при подсчете размера - } - return 0 -} - -# Функция для копирования профиля -function Copy-ThunderbirdProfile { - param( - [string]$Source, - [string]$Destination - ) - - Write-Log "Начало копирования профиля..." -Type "INFO" - Write-Log " Источник: $Source" -Type "INFO" - Write-Log " Назначение: $Destination" -Type "INFO" - - # Проверяем существование исходной папки - if (-not (Test-Path $Source)) { - Write-Log "Папка профиля НЕ НАЙДЕНА: $Source" -Type "ERROR" - return $false - } - - # Получаем размер исходной папки - $sourceSizeMB = Get-FolderSize -Path $Source - Write-Log "Размер исходной папки: ~$sourceSizeMB MB" -Type "INFO" - - # Проверяем свободное место - $requiredSpace = [math]::Max($MinFreeSpaceMB, $sourceSizeMB + 100) # +100 МБ запас - if (-not (Test-FreeSpace -Path $Destination -RequiredSpaceMB $requiredSpace)) { - Write-Log "Недостаточно места для копирования. Операция отменена." -Type "ERROR" - return $false - } - - # СОЗДАЕМ ПАПКУ НАЗНАЧЕНИЯ (ключевое изменение!) - if (-not (New-BackupFolder -FolderPath $Destination)) { - return $false - } - - # Получаем список всех профилей в папке Profiles - $profiles = Get-ChildItem -Path $Source -Directory -ErrorAction SilentlyContinue - - if ($profiles.Count -eq 0) { - Write-Log "В папке Profiles не найдено профилей!" -Type "WARNING" - Write-Log "Проверяем наличие файлов напрямую..." -Type "INFO" - - # Если нет подпапок, возможно копируем содержимое самой папки Profiles - $files = Get-ChildItem -Path $Source -File - if ($files.Count -gt 0) { - Write-Log "Найдены файлы в корне папки Profiles, копируем их" -Type "INFO" - - # Пытаемся скопировать содержимое папки Profiles - $robocopyArgs = @( - "`"$Source`"", - "`"$Destination`"", - "/E", # Копировать подпапки, включая пустые - "/COPY:DAT", # Копировать данные, атрибуты, временные метки - "/R:3", # Retry count - "/W:5", # Wait time - "/NP", # No Progress - "/NJH", # No Job Header - "/NJS" # No Job Summary - ) - - try { - $robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow - - if ($robocopyResult.ExitCode -ge 8) { - throw "Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)" - } - - Write-Log "Файлы скопированы" -Type "SUCCESS" - return $true - - } catch { - Write-Log "Ошибка при копировании: $_" -Type "ERROR" - return $false - } - } else { - Write-Log "Папка Profiles пуста" -Type "WARNING" - return $false - } - } - - # Копируем каждый профиль с повторами при ошибках - $attempt = 1 - $allCopied = $false - - while ($attempt -le $CopyRetryCount -and -not $allCopied) { - try { - Write-Log "Попытка копирования #$attempt из $CopyRetryCount..." - - $successCount = 0 - $failCount = 0 - - foreach ($profile in $profiles) { - $profileName = $profile.Name - $sourceProfilePath = $profile.FullName - $destProfilePath = Join-Path -Path $Destination -ChildPath $profileName - - Write-Log " Копирование профиля: $profileName" - - # Используем Robocopy для надежного копирования - $robocopyArgs = @( - "`"$sourceProfilePath`"", - "`"$destProfilePath`"", - "/MIR", # Mirror (точная копия) - "/R:3", # Retry count (3 повтора при ошибке) - "/W:5", # Wait time between retries (5 сек) - "/NP", # No Progress (чтобы не засорять вывод) - "/NJH", # No Job Header - "/NJS" # No Job Summary - ) - - Write-Log " Команда: robocopy $sourceProfilePath $destProfilePath /MIR /R:3 /W:5" -Type "INFO" - - $robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow - - # Robocopy возвращает коды: 0-7 - успех, 8+ - ошибка - if ($robocopyResult.ExitCode -ge 8) { - Write-Log " Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)" -Type "ERROR" - $failCount++ - } else { - Write-Log " Профиль $profileName скопирован (код: $($robocopyResult.ExitCode))" -Type "SUCCESS" - $successCount++ - } - } - - if ($failCount -eq 0) { - $allCopied = $true - Write-Log "Все профили успешно скопированы! ($successCount профилей)" -Type "SUCCESS" - } else { - Write-Log "Скопировано: $successCount, Ошибок: $failCount" -Type "WARNING" - - if ($attempt -lt $CopyRetryCount) { - Write-Log "Повторная попытка через 5 секунд..." -Type "INFO" - Start-Sleep -Seconds 5 - } - } - - } catch { - Write-Log "Ошибка при копировании (попытка $attempt): $_" -Type "ERROR" - $failCount = $profiles.Count - } - - $attempt++ - } - - return ($allCopied -or ($successCount -gt 0)) -} - -# Функция для создания информационного файла о бэкапе -function New-BackupInfoFile { - param( - [string]$BackupPath, - [string]$SourcePath - ) - - try { - $infoFile = Join-Path -Path $BackupPath -ChildPath "_backup_info.txt" - - $computerInfo = @( - "# Информация о резервной копии Thunderbird", - "Дата создания: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')", - "Компьютер: $env:COMPUTERNAME", - "Пользователь: $env:USERNAME", - "Исходная папка: $SourcePath", - "Размер исходной папки: $(Get-FolderSize -Path $SourcePath) MB", - "---", - "Создано скриптом: $($MyInvocation.MyCommand.Path)" - ) - - $computerInfo | Out-File -FilePath $infoFile -Encoding UTF8 - Write-Log "Создан информационный файл: _backup_info.txt" -Type "INFO" - - } catch { - Write-Log "Не удалось создать информационный файл: $_" -Type "WARNING" - } -} - -# === ОСНОВНАЯ ЧАСТЬ СКРИПТА === - -Clear-Host -Write-Log "===================================================" -Type "INFO" -Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ПРОФИЛЯ THUNDERBIRD" -Type "INFO" -Write-Log "===================================================" -Type "INFO" -Write-Log "" - -# 1. Проверяем и создаем корневую папку для бэкапов, если нужно -Write-Log "Проверка корневой папки для бэкапов: $BackupRoot" -Type "INFO" -New-BackupFolder -FolderPath $BackupRoot | Out-Null -Write-Log "" - -# 2. Проверяем наличие исходной папки -if (-not (Test-Path $SourceProfilePath)) { - Write-Log "Исходная папка не найдена: $SourceProfilePath" -Type "ERROR" - Write-Log "Проверьте путь и права доступа" -Type "INFO" - exit 1 -} - -# 3. Закрываем Thunderbird -Write-Log "Этап 1: Закрытие Thunderbird" -Type "INFO" -Write-Log "------------------------------" -Type "INFO" -$closed = Stop-ThunderbirdGracefully -Write-Log "" - -# 4. Небольшая пауза для гарантии -Start-Sleep -Seconds 2 - -# 5. Копируем профили -Write-Log "Этап 2: Копирование профилей" -Type "INFO" -Write-Log "-----------------------------" -Type "INFO" -$copySuccess = Copy-ThunderbirdProfile -Source $SourceProfilePath -Destination $BackupDestination -Write-Log "" - -# 6. Создаем информационный файл (если копирование успешно) -if ($copySuccess) { - New-BackupInfoFile -BackupPath $BackupDestination -SourcePath $SourceProfilePath - - # 7. Итог - Write-Log "===================================================" -Type "SUCCESS" - Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ УСПЕШНО ЗАВЕРШЕНО" -Type "SUCCESS" - Write-Log "===================================================" -Type "SUCCESS" - Write-Log "Папка с резервной копией: $BackupDestination" -Type "INFO" - - # Показываем содержимое папки с бэкапом - if (Test-Path $BackupDestination) { - $items = Get-ChildItem -Path $BackupDestination - Write-Log "Содержимое папки с бэкапом:" -Type "INFO" - foreach ($item in $items) { - $itemSize = if ($item.PSIsContainer) { - "<папка>" - } else { - "$([math]::Round($item.Length / 1KB, 2)) KB" - } - Write-Log " - $($item.Name) ($itemSize)" -Type "INFO" - } - } - - # Предложение открыть папку - $openFolder = Read-Host "`nОткрыть папку с резервной копией? (Y/N)" - if ($openFolder -eq "Y" -or $openFolder -eq "y") { - Invoke-Item $BackupDestination - } - -} else { - Write-Log "===================================================" -Type "ERROR" - Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ЗАВЕРШИЛОСЬ С ОШИБКАМИ" -Type "ERROR" - Write-Log "===================================================" -Type "ERROR" - exit 1 -} - -# Для запуска с параметрами командной строки (раскомментируйте если нужно): -# param( -# [string]$BackupPath = "D:\Backups\Thunderbird\$(Get-Date -Format 'yyyy-MM-dd')" -# ) +<# +.SYNOPSIS + Скрипт для закрытия Thunderbird и резервного копирования профиля +.DESCRIPTION + Выполняет поиск процессов Thunderbird, завершает их, ожидает полного закрытия + и создает копию папки профиля в указанное место. Автоматически создает папки назначения. +.NOTES + Author: RobinHood + Date: $(Get-Date -Format 'dd.MM.yyyy') +#> + +# === НАСТРОЙКИ === +$ThunderbirdProcessName = "thunderbird" # Имя процесса без .exe +$SourceProfilePath = "$env:APPDATA\Thunderbird\Profiles" # Стандартный путь к профилям + +# Настройка пути для резервной копии (папка создастся автоматически) +$BackupRoot = "E:\Backups\Thunderbird" # Корневая папка для бэкапов +$BackupDestination = Join-Path -Path $BackupRoot -ChildPath (Get-Date -Format 'yyyy-MM-dd') # Папка с датой + +$MaxWaitTimeSeconds = 30 # Максимальное время ожидания закрытия Thunderbird (в секундах) +$CopyRetryCount = 3 # Количество попыток копирования при ошибках +$MinFreeSpaceMB = 500 # Минимальное свободное место на диске назначения (МБ) +# ================= + +# Функция для записи лога +function Write-Log { + param( + [string]$Message, + [string]$Type = "INFO" + ) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + + # Цвета для разных типов сообщений + switch ($Type) { + "SUCCESS" { $color = "Green" } + "ERROR" { $color = "Red" } + "WARNING" { $color = "Yellow" } + "INFO" { $color = "Cyan" } + default { $color = "White" } + } + + Write-Host "[$timestamp] [$Type] " -NoNewline + Write-Host "$Message" -ForegroundColor $color +} + +# Функция для создания папки назначения +function New-BackupFolder { + param( + [string]$FolderPath + ) + + try { + if (-not (Test-Path $FolderPath)) { + Write-Log "Папка назначения не существует. Создание: $FolderPath" -Type "INFO" + + # Создаем все папки в пути рекурсивно + New-Item -Path $FolderPath -ItemType Directory -Force | Out-Null + + Write-Log "Папка успешно создана" -Type "SUCCESS" + } else { + Write-Log "Папка назначения уже существует: $FolderPath" -Type "INFO" + } + + return $true + } catch { + Write-Log "ОШИБКА при создании папки назначения: $_" -Type "ERROR" + return $false + } +} + +# Функция для проверки свободного места на диске +function Test-FreeSpace { + param( + [string]$Path, + [long]$RequiredSpaceMB = 500 + ) + + try { + # Получаем корневой диск из пути + $rootPath = Split-Path -Path $Path -Qualifier + if ([string]::IsNullOrEmpty($rootPath)) { + $rootPath = $Path[0] + ":" + } + + $drive = Get-PSDrive -Name $rootPath[0] -ErrorAction Stop + $freeSpaceMB = [math]::Round($drive.Free / 1MB, 2) + + Write-Log "Свободное место на диске $rootPath : $freeSpaceMB MB" -Type "INFO" + + if ($drive.Free -lt ($RequiredSpaceMB * 1MB)) { + Write-Log "НЕДОСТАТОЧНО МЕСТА! Требуется: $RequiredSpaceMB MB, доступно: $freeSpaceMB MB" -Type "ERROR" + return $false + } + + Write-Log "Места достаточно: $freeSpaceMB MB свободно" -Type "SUCCESS" + return $true + } catch { + Write-Log "Не удалось проверить свободное место: $_" -Type "WARNING" + return $true # Продолжаем, если не удалось проверить + } +} + +# Функция для проверки, запущен ли Thunderbird +function Test-ThunderbirdRunning { + $processes = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue + return ($processes.Count -gt 0) +} + +# Функция для закрытия Thunderbird +function Stop-ThunderbirdGracefully { + Write-Log "Поиск запущенных процессов Thunderbird..." -Type "INFO" + + if (Test-ThunderbirdRunning) { + $processCount = (Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue).Count + Write-Log "Найдено процессов Thunderbird: $processCount. Попытка корректного завершения..." -Type "WARNING" + + # Пытаемся закрыть окна Thunderbird (более мягкий способ) + $thunderbirdWindows = Get-Process -Name $ThunderbirdProcessName | Where-Object { $_.MainWindowTitle -ne "" } + + if ($thunderbirdWindows) { + foreach ($proc in $thunderbirdWindows) { + $windowTitle = if ($proc.MainWindowTitle) { $proc.MainWindowTitle } else { "Без заголовка" } + Write-Log "Закрытие окна: $windowTitle" + $proc.CloseMainWindow() | Out-Null + } + } else { + Write-Log "Нет видимых окон Thunderbird, но процессы запущены" -Type "WARNING" + } + + # Даем время на закрытие + $waitCounter = 0 + Write-Host "" + while ((Test-ThunderbirdRunning) -and ($waitCounter -lt $MaxWaitTimeSeconds)) { + Start-Sleep -Seconds 1 + $waitCounter++ + + # Простой индикатор прогресса + $progress = [math]::Round(($waitCounter / $MaxWaitTimeSeconds) * 100) + Write-Host "`rОжидание закрытия Thunderbird: $progress% " -NoNewline + } + Write-Host "" + + # Если всё ещё запущен - принудительно завершаем + if (Test-ThunderbirdRunning) { + Write-Log "Thunderbird не закрылся за отведенное время. Принудительное завершение..." -Type "ERROR" + + $remainingProcesses = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue + foreach ($proc in $remainingProcesses) { + try { + Stop-Process -Id $proc.Id -Force + Write-Log "Процесс PID $($proc.Id) принудительно завершен" -Type "WARNING" + } catch { + Write-Log "Не удалось завершить процесс PID $($proc.Id): $_" -Type "ERROR" + } + } + + Start-Sleep -Seconds 3 + } else { + Write-Log "Thunderbird успешно закрыт" -Type "SUCCESS" + } + } else { + Write-Log "Thunderbird не запущен" -Type "INFO" + } + + # Финальная проверка + if (Test-ThunderbirdRunning) { + Write-Log "ВНИМАНИЕ: Некоторые процессы Thunderbird всё еще запущены!" -Type "ERROR" + + # Показываем оставшиеся процессы + Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue | + ForEach-Object { Write-Log " - PID: $($_.Id), Запущен: $($_.StartTime)" -Type "ERROR" } + + Write-Log "Продолжаем копирование, но некоторые файлы могут быть заблокированы" -Type "WARNING" + return $false + } + + return $true +} + +# Функция для получения размера папки +function Get-FolderSize { + param([string]$Path) + + try { + if (Test-Path $Path) { + $size = (Get-ChildItem $Path -Recurse -ErrorAction SilentlyContinue | + Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum + if ($size) { + return [math]::Round($size / 1MB, 2) + } + } + } catch { + # Игнорируем ошибки при подсчете размера + } + return 0 +} + +# Функция для копирования профиля +function Copy-ThunderbirdProfile { + param( + [string]$Source, + [string]$Destination + ) + + Write-Log "Начало копирования профиля..." -Type "INFO" + Write-Log " Источник: $Source" -Type "INFO" + Write-Log " Назначение: $Destination" -Type "INFO" + + # Проверяем существование исходной папки + if (-not (Test-Path $Source)) { + Write-Log "Папка профиля НЕ НАЙДЕНА: $Source" -Type "ERROR" + return $false + } + + # Получаем размер исходной папки + $sourceSizeMB = Get-FolderSize -Path $Source + Write-Log "Размер исходной папки: ~$sourceSizeMB MB" -Type "INFO" + + # Проверяем свободное место + $requiredSpace = [math]::Max($MinFreeSpaceMB, $sourceSizeMB + 100) # +100 МБ запас + if (-not (Test-FreeSpace -Path $Destination -RequiredSpaceMB $requiredSpace)) { + Write-Log "Недостаточно места для копирования. Операция отменена." -Type "ERROR" + return $false + } + + # СОЗДАЕМ ПАПКУ НАЗНАЧЕНИЯ (ключевое изменение!) + if (-not (New-BackupFolder -FolderPath $Destination)) { + return $false + } + + # Получаем список всех профилей в папке Profiles + $profiles = Get-ChildItem -Path $Source -Directory -ErrorAction SilentlyContinue + + if ($profiles.Count -eq 0) { + Write-Log "В папке Profiles не найдено профилей!" -Type "WARNING" + Write-Log "Проверяем наличие файлов напрямую..." -Type "INFO" + + # Если нет подпапок, возможно копируем содержимое самой папки Profiles + $files = Get-ChildItem -Path $Source -File + if ($files.Count -gt 0) { + Write-Log "Найдены файлы в корне папки Profiles, копируем их" -Type "INFO" + + # Пытаемся скопировать содержимое папки Profiles + $robocopyArgs = @( + "`"$Source`"", + "`"$Destination`"", + "/E", # Копировать подпапки, включая пустые + "/COPY:DAT", # Копировать данные, атрибуты, временные метки + "/R:3", # Retry count + "/W:5", # Wait time + "/NP", # No Progress + "/NJH", # No Job Header + "/NJS" # No Job Summary + ) + + try { + $robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow + + if ($robocopyResult.ExitCode -ge 8) { + throw "Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)" + } + + Write-Log "Файлы скопированы" -Type "SUCCESS" + return $true + + } catch { + Write-Log "Ошибка при копировании: $_" -Type "ERROR" + return $false + } + } else { + Write-Log "Папка Profiles пуста" -Type "WARNING" + return $false + } + } + + # Копируем каждый профиль с повторами при ошибках + $attempt = 1 + $allCopied = $false + + while ($attempt -le $CopyRetryCount -and -not $allCopied) { + try { + Write-Log "Попытка копирования #$attempt из $CopyRetryCount..." + + $successCount = 0 + $failCount = 0 + + foreach ($profile in $profiles) { + $profileName = $profile.Name + $sourceProfilePath = $profile.FullName + $destProfilePath = Join-Path -Path $Destination -ChildPath $profileName + + Write-Log " Копирование профиля: $profileName" + + # Используем Robocopy для надежного копирования + $robocopyArgs = @( + "`"$sourceProfilePath`"", + "`"$destProfilePath`"", + "/MIR", # Mirror (точная копия) + "/R:3", # Retry count (3 повтора при ошибке) + "/W:5", # Wait time between retries (5 сек) + "/NP", # No Progress (чтобы не засорять вывод) + "/NJH", # No Job Header + "/NJS" # No Job Summary + ) + + Write-Log " Команда: robocopy $sourceProfilePath $destProfilePath /MIR /R:3 /W:5" -Type "INFO" + + $robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow + + # Robocopy возвращает коды: 0-7 - успех, 8+ - ошибка + if ($robocopyResult.ExitCode -ge 8) { + Write-Log " Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)" -Type "ERROR" + $failCount++ + } else { + Write-Log " Профиль $profileName скопирован (код: $($robocopyResult.ExitCode))" -Type "SUCCESS" + $successCount++ + } + } + + if ($failCount -eq 0) { + $allCopied = $true + Write-Log "Все профили успешно скопированы! ($successCount профилей)" -Type "SUCCESS" + } else { + Write-Log "Скопировано: $successCount, Ошибок: $failCount" -Type "WARNING" + + if ($attempt -lt $CopyRetryCount) { + Write-Log "Повторная попытка через 5 секунд..." -Type "INFO" + Start-Sleep -Seconds 5 + } + } + + } catch { + Write-Log "Ошибка при копировании (попытка $attempt): $_" -Type "ERROR" + $failCount = $profiles.Count + } + + $attempt++ + } + + return ($allCopied -or ($successCount -gt 0)) +} + +# Функция для создания информационного файла о бэкапе +function New-BackupInfoFile { + param( + [string]$BackupPath, + [string]$SourcePath + ) + + try { + $infoFile = Join-Path -Path $BackupPath -ChildPath "_backup_info.txt" + + $computerInfo = @( + "# Информация о резервной копии Thunderbird", + "Дата создания: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')", + "Компьютер: $env:COMPUTERNAME", + "Пользователь: $env:USERNAME", + "Исходная папка: $SourcePath", + "Размер исходной папки: $(Get-FolderSize -Path $SourcePath) MB", + "---", + "Создано скриптом: $($MyInvocation.MyCommand.Path)" + ) + + $computerInfo | Out-File -FilePath $infoFile -Encoding UTF8 + Write-Log "Создан информационный файл: _backup_info.txt" -Type "INFO" + + } catch { + Write-Log "Не удалось создать информационный файл: $_" -Type "WARNING" + } +} + +# === ОСНОВНАЯ ЧАСТЬ СКРИПТА === + +Clear-Host +Write-Log "===================================================" -Type "INFO" +Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ПРОФИЛЯ THUNDERBIRD" -Type "INFO" +Write-Log "===================================================" -Type "INFO" +Write-Log "" + +# 1. Проверяем и создаем корневую папку для бэкапов, если нужно +Write-Log "Проверка корневой папки для бэкапов: $BackupRoot" -Type "INFO" +New-BackupFolder -FolderPath $BackupRoot | Out-Null +Write-Log "" + +# 2. Проверяем наличие исходной папки +if (-not (Test-Path $SourceProfilePath)) { + Write-Log "Исходная папка не найдена: $SourceProfilePath" -Type "ERROR" + Write-Log "Проверьте путь и права доступа" -Type "INFO" + exit 1 +} + +# 3. Закрываем Thunderbird +Write-Log "Этап 1: Закрытие Thunderbird" -Type "INFO" +Write-Log "------------------------------" -Type "INFO" +$closed = Stop-ThunderbirdGracefully +Write-Log "" + +# 4. Небольшая пауза для гарантии +Start-Sleep -Seconds 2 + +# 5. Копируем профили +Write-Log "Этап 2: Копирование профилей" -Type "INFO" +Write-Log "-----------------------------" -Type "INFO" +$copySuccess = Copy-ThunderbirdProfile -Source $SourceProfilePath -Destination $BackupDestination +Write-Log "" + +# 6. Создаем информационный файл (если копирование успешно) +if ($copySuccess) { + New-BackupInfoFile -BackupPath $BackupDestination -SourcePath $SourceProfilePath + + # 7. Итог + Write-Log "===================================================" -Type "SUCCESS" + Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ УСПЕШНО ЗАВЕРШЕНО" -Type "SUCCESS" + Write-Log "===================================================" -Type "SUCCESS" + Write-Log "Папка с резервной копией: $BackupDestination" -Type "INFO" + + # Показываем содержимое папки с бэкапом + if (Test-Path $BackupDestination) { + $items = Get-ChildItem -Path $BackupDestination + Write-Log "Содержимое папки с бэкапом:" -Type "INFO" + foreach ($item in $items) { + $itemSize = if ($item.PSIsContainer) { + "<папка>" + } else { + "$([math]::Round($item.Length / 1KB, 2)) KB" + } + Write-Log " - $($item.Name) ($itemSize)" -Type "INFO" + } + } + + # Предложение открыть папку + $openFolder = Read-Host "`nОткрыть папку с резервной копией? (Y/N)" + if ($openFolder -eq "Y" -or $openFolder -eq "y") { + Invoke-Item $BackupDestination + } + +} else { + Write-Log "===================================================" -Type "ERROR" + Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ЗАВЕРШИЛОСЬ С ОШИБКАМИ" -Type "ERROR" + Write-Log "===================================================" -Type "ERROR" + exit 1 +} + +# Для запуска с параметрами командной строки (раскомментируйте если нужно): +# param( +# [string]$BackupPath = "D:\Backups\Thunderbird\$(Get-Date -Format 'yyyy-MM-dd')" +# ) # $BackupDestination = $BackupPath \ No newline at end of file