Public/Install-1CPlatform.ps1

<#
.SYNOPSIS
    Устанавливает/обновляет платформу 1С (включая сервер и администрирование).
 
.DESCRIPTION
    Поддерживает три сценария источника:
      1) -SetupPath указывает на УЖЕ РАСПАКОВАННЫЙ дистрибутив (внутри есть '1CEnterprise 8 (x86-64).msi'):
         → используем этот каталог напрямую, архивы НЕ ищем и НЕ копируем.
      2) -SetupPath указывает на корень/папку с архивами:
         → (при -Version) ищем ТОЛЬКО 'windows64full_<версия>.rar' рекурсивно; (без -Version) — берём самый новый архив.
         → копируем архив в C:\1Cv8.adm и распаковываем через New-1CDistroPackage.
      3) -SetupPath не задан:
         → используем C:\1Cv8.adm (Find-1CDistroFolder) и распаковку через New-1CDistroPackage.
 
    При -Version выполняется строгая проверка: установленная/распакованная версия = указанной.
    Порядок: MSI → проверка exit-code → New-1CCurrentPlatformLink → регистрация DLL из current\bin.
 
.PARAMETER SetupPath
    Папка конкретной версии (распакованной) ИЛИ корень с архивами (UNC/локальный).
 
.PARAMETER Version
    Точная версия, например 8.3.22.1704. Если задана — ставим ровно этот билд.
 
.PARAMETER Quiet
    Тихая установка (/qn). По умолчанию $true.
 
.PARAMETER NoRestart
    Без перезагрузки (/norestart). По умолчанию $true.
#>

function Install-1CPlatform {
    [CmdletBinding()]
    param(
        [string]$SetupPath,
        [string]$Version,
        [bool]$Quiet = $true,
        [bool]$NoRestart = $true
    )

    # Требуются права администратора
    if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
        ).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        throw "Требуются права администратора."
    }

    # Базовые переменные
    $admPath = Find-1CDistroFolder
    if (-not $admPath -or -not (Test-Path -LiteralPath $admPath)) {
        throw "Каталог дистрибутивов 1Cv8.adm не найден."
    }

    $useExtracted = $false
    $installDir   = $null   # каталог, где лежит MSI (итоговый источник для установки)
    $pkgVer       = $null   # обнаруженная версия
    $msiName      = '1CEnterprise 8 (x86-64).msi'

    # --- ВЕТКА А: SetupPath задан и это уже распакованный дистрибутив (есть MSI) ---
    if ($SetupPath -and (Test-Path -LiteralPath $SetupPath)) {
        $msiProbe = Join-Path $SetupPath $msiName
        if (-not (Test-Path -LiteralPath $msiProbe)) {
            # Может быть, пользователь указал ..\Server\64 или на уровень выше/ниже
            $msiProbe = Get-ChildItem -LiteralPath $SetupPath -Recurse -Filter $msiName -File -ErrorAction SilentlyContinue |
                        Select-Object -First 1 | ForEach-Object FullName
        }
        if ($msiProbe) {
            # Это уже распакованный дистрибутив
            $useExtracted = $true
            $installDir   = Split-Path -Path $msiProbe -Parent

            # Попробуем извлечь версию из пути: ...\<8.3.xx.xxxx>\Server\64
            $pathParts = $installDir -split '[\\/]'
            $maybeVer  = $pathParts | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } | Select-Object -Last 1
            if ($maybeVer) { $pkgVer = $maybeVer }

            Write-Host "Обнаружен уже распакованный дистрибутив: $installDir" -ForegroundColor Cyan

            if ($Version) {
                if ($pkgVer) {
                    if ($pkgVer -ne $Version) {
                        throw "Несовпадение версии: в пути '$pkgVer', ожидается '$Version'. Укажите корректный SetupPath или версию."
                    }
                }
                else {
                    Write-Warning "Не удалось определить версию из пути '$installDir'. Продолжаю без сверки с -Version."
                }
            }
        }
    }

    if (-not $useExtracted) {
        # --- ВЕТКА B/C: требуется работа с архивом и New-1CDistroPackage ---
        # Если задана конкретная версия — найдём ТОЛЬКО соответствующий архив и скопируем в 1Cv8.adm
        if ($PSBoundParameters.ContainsKey('Version')) {
            if (-not ($Version -match '^\d+\.\d+\.\d+\.\d+$')) {
                throw "Некорректная версия: '$Version'. Ожидается формат 8.3.xx.xxxx."
            }
            $verU = $Version -replace '\.', '_'
            $wanted = "windows64full_${verU}.rar"

            $src = $null
            if ($SetupPath) {
                if (-not (Test-Path -LiteralPath $SetupPath)) { throw "Каталог не найден: $SetupPath" }
                $src = Get-ChildItem -LiteralPath $SetupPath -Recurse -Filter $wanted -File -ErrorAction SilentlyContinue |
                       Select-Object -First 1 | ForEach-Object FullName
            } else {
                # допускаем, что архив уже лежит в 1Cv8.adm
                $src = Get-ChildItem -LiteralPath $admPath -Filter $wanted -File -ErrorAction SilentlyContinue |
                       Select-Object -First 1 | ForEach-Object FullName
            }
            if (-not $src) { throw "Не найден архив '$wanted' в '$SetupPath'. Поместите архив в $admPath или укажите корректный -SetupPath." }

            $dst = Join-Path $admPath (Split-Path -Leaf $src)
            if ($src -ne $dst) {
                Write-Host "Копирую архив конкретной версии: '$src' → '$dst'" -ForegroundColor Yellow
                Copy-Item -LiteralPath $src -Destination $dst -Force
            } else {
                Write-Host "Архив нужной версии уже в 1Cv8.adm: $dst" -ForegroundColor DarkGray
            }
        }
        elseif ($PSBoundParameters.ContainsKey('SetupPath') -and $SetupPath) {
            if (-not (Test-Path -LiteralPath $SetupPath)) { throw "Каталог не найден: $SetupPath" }
            $cands = Get-ChildItem -LiteralPath $SetupPath -Recurse -Filter 'windows64full_8_3_*.rar' -File -ErrorAction SilentlyContinue
            if ($cands) {
                $parsed = foreach ($f in $cands) {
                    if ($f.BaseName -match '^windows64full_(?<v>\d+_\d+_\d+_\d+)$') {
                        $v = $matches['v'] -replace '_','.'
                        try { [pscustomobject]@{ Version=[version]$v; File=$f.FullName } } catch {}
                    }
                }
                if ($parsed) {
                    $best = $parsed | Sort-Object Version -Descending | Select-Object -First 1
                    $dst  = Join-Path $admPath (Split-Path -Leaf $best.File)
                    if (-not (Test-Path -LiteralPath $dst)) {
                        Write-Host "Копирую архив: '$($best.File)' → '$dst'" -ForegroundColor Yellow
                        Copy-Item -LiteralPath $best.File -Destination $dst -Force
                    } else {
                        Write-Host "Архив уже есть в 1Cv8.adm: $dst" -ForegroundColor DarkGray
                    }
                } else {
                    throw "Не найден серверный архив windows64full_8_3_xx_xxxx.rar в '$SetupPath'."
                }
            } else {
                throw "Не найден серверный архив windows64full_8_3_xx_xxxx.rar в '$SetupPath'."
            }
        }
        else {
            Write-Host "SetupPath не задан — будет использован локальный кэш 1Cv8.adm." -ForegroundColor DarkGray
        }

        # Распаковка/подготовка пакета
        Write-Host "Подготовка дистрибутива (New-1CDistroPackage)..." -ForegroundColor Cyan
        $pkg = New-1CDistroPackage
        if (-not $pkg) { throw "Не удалось подготовить дистрибутив." }

        $installDir = $pkg.Path
        $pkgVer     = $pkg.VersionString
        Write-Host "Обнаружен пакет версии: $pkgVer" -ForegroundColor DarkGray

        if ($Version -and $pkgVer -ne $Version) {
            throw "Распакована версия '$pkgVer', ожидалась '$Version'. Установка остановлена."
        }

        # Удалим архив соответствующей версии из 1Cv8.adm (если остался)
        if ($pkgVer) {
            $verU = $pkgVer -replace '\.', '_'
            Get-ChildItem -LiteralPath $admPath -Filter "windows64full_${verU}.rar" -File -ErrorAction SilentlyContinue |
                ForEach-Object {
                    Write-Host "Удаляю архив: $($_.FullName)" -ForegroundColor DarkGray
                    Remove-Item -LiteralPath $_.FullName -Force
                }
        }
    }

    # --- Установка MSI из $installDir ---
    $msi = Join-Path $installDir $msiName
    if (-not (Test-Path -LiteralPath $msi)) {
        throw "MSI не найден: $msi"
    }

    Push-Location $installDir
    try {
        $admMst = Join-Path $installDir 'adminstallrelogon.mst'
        $ruMst  = Join-Path $installDir '1049.mst'
        $trans  = @()
        if (Test-Path -LiteralPath $admMst) { $trans += $admMst }
        if (Test-Path -LiteralPath $ruMst)  { $trans += $ruMst }

        $params = @('/i', $msi)
        if ($Quiet)     { $params += '/qn' }
        if ($NoRestart) { $params += '/norestart' }
        if ($trans.Count) { $params += "TRANSFORMS=$($trans -join ';')" }

        # Явные свойства — сервер и администрирование точно установятся
        $params += @(
            'DESIGNERALLCLIENTS=1',
            'THICKCLIENT=1',
            'THINCLIENTFILE=1',
            'THINCLIENT=1',
            'WEBSERVEREXT=1',
            'SERVER=1',
            'CONFREPOSSERVER=0',
            'CONVERTER77=0',
            'SERVERCLIENT=1',
            'LANGUAGES=RU'
        )

        Write-Host "Выполняется установка 1С $($pkgVer ?? '(версия не определена)')..." -ForegroundColor Green
        & msiexec.exe @params
        $code = $LASTEXITCODE
        if     ($code -eq 0)    { Write-Host "MSI: успех (0)." -ForegroundColor Green }
        elseif ($code -eq 3010) { Write-Host "MSI: успех, требуется перезагрузка (3010)." -ForegroundColor Yellow }
        else                    { throw "MSI завершился с кодом $code." }
    }
    finally {
        Pop-Location
    }

    # Обновляем ссылку current → затем регистрируем DLL по ссылке current
    New-1CCurrentPlatformLink

    $binCurrent = 'C:\Program Files\1cv8\current\bin'
    $comcntr = Join-Path $binCurrent 'comcntr.dll'
    $radmin  = Join-Path $binCurrent 'radmin.dll'
    foreach ($dll in @($comcntr, $radmin)) {
        if (Test-Path -LiteralPath $dll) {
            & regsvr32.exe "`"$dll`"" -s
        } else {
            Write-Warning "Файл не найден: $dll"
        }
    }
}