Public/ps1/Configuration/General/Get-ApprxrInfo.ps1

<#
.SYNOPSIS
    Returns a consolidated overview of the local Apprxr setup.
 
.DESCRIPTION
    Collects service, version, file watcher, and nginx proxy information.
    By default this command returns a structured object.
    Use -AsJson for remote processing and -UserFriendly for readable console output.
 
.PARAMETER AsJson
    Returns the collected information as a JSON string.
 
.PARAMETER UserFriendly
    Prints a readable summary to the console and also returns the structured object.
 
.EXAMPLE
    Get-ApprxrInfo
 
.EXAMPLE
    Get-ApprxrInfo -AsJson
 
.EXAMPLE
    Get-ApprxrInfo -UserFriendly
#>

function Get-ApprxrInfo {
    [CmdletBinding()]
    param(
        [switch]$AsJson,
        [switch]$UserFriendly
    )

    if ($AsJson -and $UserFriendly) {
        throw 'Use either -AsJson or -UserFriendly, not both.'
    }

    $moduleVersion = 'Unknown'
    try {
        $loadedModule = Get-Module Apprxr -ErrorAction Stop
        if ($loadedModule -and $loadedModule.Version) {
            $moduleVersion = $loadedModule.Version.ToString()
        }
    } catch {
        try {
            $moduleManifestPath = Join-Path $PSScriptRoot '..\..\..\..\Apprxr.psd1'
            $moduleManifest = Test-ModuleManifest -Path $moduleManifestPath -ErrorAction Stop
            if ($moduleManifest -and $moduleManifest.Version) {
                $moduleVersion = $moduleManifest.Version.ToString()
            }
        } catch {}
    }

    $configFolder = Get-ApprxrConfigurationFolder
    $configFilePath = Join-Path $configFolder 'config.json'
    $configFileExists = Test-Path -LiteralPath $configFilePath

    $serviceNameExtension = [string](@(Get-ApprxrConfigurationValue -name 'ServiceNameExtension')[0])
    $serviceExtension = [string](@(Get-ApprxrConfigurationValue -name 'ServiceExtension')[0])

    $configuredExtensions = @()
    if ($serviceNameExtension) { $configuredExtensions += $serviceNameExtension }
    if ($serviceExtension -and ($configuredExtensions -notcontains $serviceExtension)) {
        $configuredExtensions += $serviceExtension
    }

    $configuredServiceNames = @('ApprxrService')
    foreach ($extension in $configuredExtensions) {
        $configuredServiceNames += "ApprxrService$extension"
        $configuredServiceNames += "ApprxrService_$extension"
    }
    $configuredServiceNames = @($configuredServiceNames | Select-Object -Unique)

    $services = @()
    try {
        $services = @(Get-CimInstance -ClassName Win32_Service -Filter "Name LIKE 'ApprxrService%'" -ErrorAction Stop |
            Sort-Object Name |
            ForEach-Object {
                [pscustomobject]@{
                    Name      = $_.Name
                    State     = $_.State
                    StartMode = $_.StartMode
                    StartName = $_.StartName
                    PathName  = $_.PathName
                }
            })
    } catch {
        $services = @(Get-Service -Name 'ApprxrService*' -ErrorAction SilentlyContinue |
            Sort-Object Name |
            ForEach-Object {
                [pscustomobject]@{
                    Name      = $_.Name
                    State     = $_.Status.ToString()
                    StartMode = $null
                    StartName = $null
                    PathName  = $null
                }
            })
    }

    $serviceUsers = @($services |
        Select-Object -ExpandProperty StartName -Unique -ErrorAction SilentlyContinue |
        Where-Object { $_ })

    $nginxDirRaw = @(Get-ApprxrConfigurationValue -name NginxProxyDir)[0]
    $nginxDir = if ($null -eq $nginxDirRaw) { '' } else { [string]$nginxDirRaw }
    $nginxDir = $nginxDir.Trim()
    $nginxPortRaw = @(Get-ApprxrConfigurationValue -name NginxProxyPort)[0]
    $nginxPort = $null
    if ($null -ne $nginxPortRaw -and "$nginxPortRaw" -ne '') {
        $nginxPort = [int]$nginxPortRaw
    }
    $nginxEnabled = [bool](@(Get-ApprxrConfigurationValue -name NginxProxyEnabled)[0])
    $nginxExePath = if ($nginxDir) { Join-Path $nginxDir 'nginx.exe' } else { $null }
    $nginxConfigPath = if ($nginxDir) { Join-Path $nginxDir 'conf\nginx.conf' } else { $null }
    $nginxExeExists = [bool]($nginxExePath -and (Test-Path -LiteralPath $nginxExePath))
    $nginxConfigExists = [bool]($nginxConfigPath -and (Test-Path -LiteralPath $nginxConfigPath))
    # Scope to processes whose exe path matches this installation directory AND is
    # under the current user's LOCALAPPDATA so that multiple service-account deployments
    # (e.g. acc + production) don't see each other.
    $nginxProcesses = @(Get-Process -Name 'nginx' -ErrorAction SilentlyContinue |
        Where-Object { try { $_.MainModule.FileName -ieq $nginxExePath -and
                             $_.MainModule.FileName -ilike "$env:LOCALAPPDATA\*" } catch { $false } })

    $nginxUpstreamHost = $null
    if ($nginxConfigExists) {
        try {
            $proxyPassLine = Get-Content -LiteralPath $nginxConfigPath | Where-Object { $_ -match 'proxy_pass\s+https?://([^;]+);' } | Select-Object -First 1
            if ($proxyPassLine) {
                $match = [regex]::Match($proxyPassLine, 'proxy_pass\s+https?://([^;]+);')
                if ($match.Success) {
                    $nginxUpstreamHost = $match.Groups[1].Value
                }
            }
        } catch {}
    }

    $rawFileWatcher = Get-ApprxrConfigurationValue -name 'FileWatcher'
    $fileWatcherLocations = @()
    if ($rawFileWatcher) {
        try {
            $fileWatcherObject = $rawFileWatcher
            if ($rawFileWatcher -is [string]) {
                $fileWatcherObject = $rawFileWatcher | ConvertFrom-Json -ErrorAction Stop
            }

            if ($fileWatcherObject -and $fileWatcherObject.PSObject -and $fileWatcherObject.PSObject.Properties) {
                foreach ($property in $fileWatcherObject.PSObject.Properties) {
                    $locationName = $property.Name
                    $locationConfig = $property.Value

                    $filters = @()
                    if ($locationConfig -and $locationConfig.PSObject -and $locationConfig.PSObject.Properties['Filter']) {
                        if ($locationConfig.Filter -is [System.Array]) {
                            $filters = @($locationConfig.Filter)
                        } elseif ($null -ne $locationConfig.Filter) {
                            $filters = @($locationConfig.Filter)
                        }
                    }

                    $fileWatcherLocations += [pscustomobject]@{
                        Name             = $locationName
                        InputFolder      = if ($locationConfig) { $locationConfig.InputFolder } else { $null }
                        InProgressFolder = if ($locationConfig) { $locationConfig.InProgressFolder } else { $null }
                        VdbName          = if ($locationConfig) { $locationConfig.VdbName } else { $null }
                        Filter           = $filters
                    }
                }
            }
        } catch {}
    }

    $pipelineNames = @(Get-ApprxrPipelines)
    $pipelineDetails = @()
    foreach ($pipelineName in $pipelineNames) {
        $pSleep = Get-ApprxrConfigurationValue -Section "Pipelines.$pipelineName" -name 'Sleep' -defaultValue $null
        if ($null -eq $pSleep) {
            $pSleep = Get-ApprxrConfigurationValue -name ($pipelineName + 'Sleep') -defaultValue 10
        }
        $pDirect = Get-ApprxrConfigurationValue -Section "Pipelines.$pipelineName" -name 'DirectProcessing' -defaultValue $null
        if ($null -eq $pDirect) {
            $pDirect = Get-ApprxrConfigurationValue -name ($pipelineName + 'DirectProcessing') -defaultValue $false
        }
        $pWindow = Get-ApprxrConfigurationValue -Section "Pipelines.$pipelineName" -name 'WindowSize' -defaultValue 10
        $pMaxTP = Get-ApprxrConfigurationValue -Section "Pipelines.$pipelineName" -name 'MaxThroughput' -defaultValue 5
        $pipelineDetails += [pscustomobject]@{
            Name             = $pipelineName
            Sleep            = $pSleep
            DirectProcessing = $pDirect
            WindowSize       = $pWindow
            MaxThroughput    = $pMaxTP
        }
    }

    $runningPipelineJobs = @(Get-Job -ErrorAction SilentlyContinue |
        Where-Object { $_.Name -like 'Apprxr*' -and $_.State -eq 'Running' -and $_.Name -notlike '*FileWatcher*' } |
        Select-Object -ExpandProperty Name)

    $runningWatcherJobs = @(Get-Job -ErrorAction SilentlyContinue |
        Where-Object { $_.Name -like 'Apprxr*' -and $_.State -eq 'Running' } |
        Select-Object -ExpandProperty Name)

    $watcherStateFolder = if ($null -ne $LogManagement -and $LogManagement.LogFolder) {
        Join-Path (Split-Path $LogManagement.LogFolder -Parent) 'watcher-state'
    } else {
        Join-Path $env:TEMP 'Apprxr\watcher-state'
    }

    $watcherStateFileCount = 0
    if (Test-Path -LiteralPath $watcherStateFolder) {
        $watcherStateFileCount = @(Get-ChildItem -Path $watcherStateFolder -Filter '*.json' -ErrorAction SilentlyContinue).Count
    }

    $result = [pscustomobject]@{
        GeneratedAt   = (Get-Date).ToString('yyyy-MM-ddTHH:mm:ss')
        Version       = $moduleVersion
        Configuration = [pscustomobject]@{
            Folder         = $configFolder
            ConfigFilePath = $configFilePath
            ConfigFileExists = $configFileExists
        }
        Service       = [pscustomobject]@{
            IsConfigured         = ($services.Count -gt 0 -or $configuredExtensions.Count -gt 0)
            ConfiguredExtensions = $configuredExtensions
            ConfiguredNames      = $configuredServiceNames
            Services             = $services
            Users                = $serviceUsers
        }
        Pipelines     = [pscustomobject]@{
            Count           = $pipelineDetails.Count
            Pipelines       = $pipelineDetails
            RunningJobNames = $runningPipelineJobs
        }
        FileWatcher   = [pscustomobject]@{
            LocationCount   = $fileWatcherLocations.Count
            Locations       = $fileWatcherLocations
            RunningJobNames = $runningWatcherJobs
            StateFolder     = $watcherStateFolder
            StateFileCount  = $watcherStateFileCount
        }
        Nginx         = [pscustomobject]@{
            Enabled      = $nginxEnabled
            Port         = $nginxPort
            Directory    = $nginxDir
            ExePath      = $nginxExePath
            ExeExists    = $nginxExeExists
            ConfigPath   = $nginxConfigPath
            ConfigExists = $nginxConfigExists
            UpstreamHost = $nginxUpstreamHost
            Running      = ($nginxProcesses.Count -gt 0)
            Pids         = @($nginxProcesses | Select-Object -ExpandProperty Id)
        }
    }

    if ($AsJson) {
        return ($result | ConvertTo-Json -Depth 10)
    }

    if ($UserFriendly) {
        Write-Host ''
        Write-Host '=== Apprxr Information ===' -ForegroundColor Cyan
        Write-Host ("Version : {0}" -f $result.Version)
        Write-Host ("Config file : {0}" -f $result.Configuration.ConfigFilePath)
        Write-Host ("Config file exists : {0}" -f $result.Configuration.ConfigFileExists)

        Write-Host ''
        Write-Host 'Service' -ForegroundColor Cyan
        Write-Host ("Configured : {0}" -f $result.Service.IsConfigured)
        Write-Host ("Configured names : {0}" -f (($result.Service.ConfiguredNames -join ', ')))
        if ($result.Service.Services.Count -eq 0) {
            Write-Host 'Installed services : none'
        } else {
            foreach ($service in $result.Service.Services) {
                Write-Host ("- {0} | State={1} | User={2}" -f $service.Name, $service.State, $service.StartName)
            }
        }

        Write-Host ''
        Write-Host 'Pipelines' -ForegroundColor Cyan
        Write-Host ("Configured pipelines : {0}" -f $result.Pipelines.Count)
        if ($result.Pipelines.Pipelines.Count -gt 0) {
            foreach ($p in $result.Pipelines.Pipelines) {
                Write-Host ("- {0} | Sleep={1} | Direct={2} | Window={3} | MaxTP={4}" -f $p.Name, $p.Sleep, $p.DirectProcessing, $p.WindowSize, $p.MaxThroughput)
            }
        }
        Write-Host ("Running pipeline jobs : {0}" -f (@($result.Pipelines.RunningJobNames).Count))

        Write-Host ''
        Write-Host 'FileWatcher' -ForegroundColor Cyan
        Write-Host ("Configured locations : {0}" -f $result.FileWatcher.LocationCount)
        if ($result.FileWatcher.Locations.Count -gt 0) {
            foreach ($location in $result.FileWatcher.Locations) {
                Write-Host ("- {0} | Input={1} | VDB={2}" -f $location.Name, $location.InputFolder, $location.VdbName)
            }
        }
        Write-Host ("Running jobs : {0}" -f (@($result.FileWatcher.RunningJobNames).Count))

        Write-Host ''
        Write-Host 'Nginx' -ForegroundColor Cyan
        Write-Host ("Enabled : {0}" -f $result.Nginx.Enabled)
        Write-Host ("Running : {0}" -f $result.Nginx.Running)
        Write-Host ("Port : {0}" -f $result.Nginx.Port)
        Write-Host ("Config : {0}" -f $result.Nginx.ConfigPath)
        Write-Host ("Upstream : {0}" -f $result.Nginx.UpstreamHost)
        Write-Host ''
    }

    return $result
}
# SIG # Begin signature block
# MIInKgYJKoZIhvcNAQcCoIInGzCCJxcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAreEHawLZGJgTT
# wUsjFLS1diz7co60HFo42aR0z15iUqCCIKYwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggaXMIIEf6ADAgECAhAfsijAKebq6quDMIHCRvIfMA0GCSqG
# SIb3DQEBCwUAMGoxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT
# eXN0ZW1zIFMuQS4xODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9u
# IENvZGUgU2lnbmluZyAyMDIxIENBMB4XDTI2MDIwMzEzNTE0NFoXDTI4MDIwMzEz
# NTE0M1owgaExHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
# BAGCNzwCAQMTAk5MMREwDwYDVQQFEwg5NjMyNDU2MjELMAkGA1UEBhMCTkwxEDAO
# BgNVBAgMB1V0cmVjaHQxEzARBgNVBAcMCkFtZXJzZm9vcnQxETAPBgNVBAoMCEFQ
# UFJYIEJWMREwDwYDVQQDDAhBUFBSWCBCVjCCAaIwDQYJKoZIhvcNAQEBBQADggGP
# ADCCAYoCggGBAOF+mHloTjmkknba8mOejw0Gq3Aczy1dtatfdLM70ken0n07zIlY
# 2jNmyTv5I5RixXAt3LJUwWnUQH8okGFJYvnLZRPyjpCxnEqq8GL6puhKjIIcTs8W
# VhoADX+mFtLXygLrfk1j6A+dnNXjoCQMsVSEYppSGrpMpKsPQONb1mmFRx66vqro
# HcP93qWYTm/f3TpRzlscIxcQ/3Ab+KLeW0sHgC/a8MVHLicbdUe/pka4jMM98Djj
# BtkBc18dI6ECtrDfB2FXXFomvgm5UyPaf2sYxMPL+X/iCNhaQevoT3vttxMvKzAD
# JxJK7VsV2nrCvxfTaP4cVd9FfoxUqjCE0eA/IH3qrKqAv3eONjMrRzoO9jJumusi
# t91wYT1dwTe/o2FAwfmZ7qxofCXTlO008weUDY5UBw8d+BavVvnDNR08eEcCJN37
# wBJn1hhjFZZD6OxVcMEoyhIG5tJqvmBPyR5A1oYeGSL+/xBbLpAQR0oAhAivxvn4
# POX2r4M/wXGJewIDAQABo4IBfzCCAXswDAYDVR0TAQH/BAIwADBBBgNVHR8EOjA4
# MDagNKAyhjBodHRwOi8vY2V2Y3NjYTIwMjEuY3JsLmNlcnR1bS5wbC9jZXZjc2Nh
# MjAyMS5jcmwwdwYIKwYBBQUHAQEEazBpMC4GCCsGAQUFBzABhiJodHRwOi8vY2V2
# Y3NjYTIwMjEub2NzcC1jZXJ0dW0uY29tMDcGCCsGAQUFBzAChitodHRwOi8vcmVw
# b3NpdG9yeS5jZXJ0dW0ucGwvY2V2Y3NjYTIwMjEuY2VyMB8GA1UdIwQYMBaAFKxX
# yggW3D/FMRwKTdv78d6ZJy00MB0GA1UdDgQWBBSEZ5pbrzKazBz6ZCRYAbuQVlDy
# pDBKBgNVHSAEQzBBMAcGBWeBDAEDMDYGCyqEaAGG9ncCBQEHMCcwJQYIKwYBBQUH
# AgEWGWh0dHBzOi8vd3d3LmNlcnR1bS5wbC9DUFMwEwYDVR0lBAwwCgYIKwYBBQUH
# AwMwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQARtX2dHRUVP9uH
# 5FuVIvj22UsNMW2gsexnRxeL/xKxY4BXv6UkQmcgABKbYlWGH0wodaKRhrT9NyK1
# tt0P1C53zAkaiwNU7gSge7s/OPk2yA7g++iVBUDXRelZVCWi31V8yRVV+zXAFEkK
# timx00QcEhLFu1DAyjABmKouChHJyqF4dHWytvQZQHw1dfG1OSh6F82thRipW/q1
# ggWzm91QdieGlaSBZy9N881+h4V23R7oJVyWcCUtar19rBJH+/TaZHMo3VDVazhr
# CYUKMf/f6RA+iuSsPcpwMi7BZzTxawQtEe/1vG3RLVP94bWl69gY8LiTe0OU23tp
# 1PuTiHFLEXjTIb9WnBljH2PvUpJVaxQAMZrunKZvuafYSAvcEfSiRvgFg3r1UH9I
# nLjJVxsjfxjnbg5y/5SChh+OBbld7+NdvWVoFUGwqK2IuALD0O2/gtQBS/+UZuiB
# GsWpTv9PWI9/C2kPjEcBjC3sZedZOjkEJNSb8siUDItoswA+ddZRBBLdkNJCIKX4
# +lrKAAXdOqGu1QSu57G/OOLyjeUHEWx73XzoPMdgGfkcbdSBQoWBevDbC2xLzqr6
# EQZhplxGR39HZ4M4GyNFcpL4Xhuw1Gma8ldGTeuNw5ktP5Jmf9zvrjP5epHgMl6r
# jO0VG4wGU+R4e/FPQClKsyxI/cTJMjCCBrQwggScoAMCAQICEA3HrFcF/yGZLkBD
# Igw6SYYwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY
# RGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTI1MDUwNzAwMDAwMFoXDTM4MDEx
# NDIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0
# MDk2IFNIQTI1NiAyMDI1IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBALR4MdMKmEFyvjxGwBysddujRmh0tFEXnU2tjQ2UtZmWgyxU7UNqEY81FzJs
# Qqr5G7A6c+Gh/qm8Xi4aPCOo2N8S9SLrC6Kbltqn7SWCWgzbNfiR+2fkHUiljNOq
# nIVD/gG3SYDEAd4dg2dDGpeZGKe+42DFUF0mR/vtLa4+gKPsYfwEu7EEbkC9+0F2
# w4QJLVSTEG8yAR2CQWIM1iI5PHg62IVwxKSpO0XaF9DPfNBKS7Zazch8NF5vp7ea
# Z2CVNxpqumzTCNSOxm+SAWSuIr21Qomb+zzQWKhxKTVVgtmUPAW35xUUFREmDrMx
# SNlr/NsJyUXzdtFUUt4aS4CEeIY8y9IaaGBpPNXKFifinT7zL2gdFpBP9qh8SdLn
# Eut/GcalNeJQ55IuwnKCgs+nrpuQNfVmUB5KlCX3ZA4x5HHKS+rqBvKWxdCyQEEG
# cbLe1b8Aw4wJkhU1JrPsFfxW1gaou30yZ46t4Y9F20HHfIY4/6vHespYMQmUiote
# 8ladjS/nJ0+k6MvqzfpzPDOy5y6gqztiT96Fv/9bH7mQyogxG9QEPHrPV6/7umw0
# 52AkyiLA6tQbZl1KhBtTasySkuJDpsZGKdlsjg4u70EwgWbVRSX1Wd4+zoFpp4Ra
# +MlKM2baoD6x0VR4RjSpWM8o5a6D8bpfm4CLKczsG7ZrIGNTAgMBAAGjggFdMIIB
# WTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTvb1NK6eQGfHrK4pBW9i/U
# SezLTjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8E
# BAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQu
# Y3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsG
# CWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAF877FoAc/gc9EXZxML2+C8i1
# NKZ/zdCHxYgaMH9Pw5tcBnPw6O6FTGNpoV2V4wzSUGvI9NAzaoQk97frPBtIj+ZL
# zdp+yXdhOP4hCFATuNT+ReOPK0mCefSG+tXqGpYZ3essBS3q8nL2UwM+NMvEuBd/
# 2vmdYxDCvwzJv2sRUoKEfJ+nN57mQfQXwcAEGCvRR2qKtntujB71WPYAgwPyWLKu
# 6RnaID/B0ba2H3LUiwDRAXx1Neq9ydOal95CHfmTnM4I+ZI2rVQfjXQA1WSjjf4J
# 2a7jLzWGNqNX+DF0SQzHU0pTi4dBwp9nEC8EAqoxW6q17r0z0noDjs6+BFo+z7bK
# SBwZXTRNivYuve3L2oiKNqetRHdqfMTCW/NmKLJ9M+MtucVGyOxiDf06VXxyKkOi
# rv6o02OoXN4bFzK0vlNMsvhlqgF2puE6FndlENSmE+9JGYxOGLS/D284NHNboDGc
# mWXfwXRy4kbu4QFhOm0xJuF2EZAOk5eCkhSxZON3rGlHqhpB/8MluDezooIs8CVn
# rpHMiD2wL40mm53+/j7tFaxYKIqL0Q4ssd8xHZnIn/7GELH3IdvG2XlM9q7WP/Uw
# gOkw/HQtyRN62JK4S1C8uw3PdBunvAZapsiI5YKdvlarEvf8EA+8hcpSM9LHJmyr
# xaFtoza2zNaQ9k+5t1wwggbNMIIEtaADAgECAhEAu/DMtbe4Mf0hrjJ3iuQMiTAN
# BgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8g
# VGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9u
# IEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAy
# MB4XDTIxMDUxOTA1MzIxM1oXDTM2MDUxODA1MzIxM1owajELMAkGA1UEBhMCUEwx
# ITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjE4MDYGA1UEAxMvQ2Vy
# dHVtIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCUvvjwlqjf9eedRTK+NoavZlVx
# mQTPIN1nK5XQRUQWyPWwHD54wa2aD2kRDrAjBf71/T0XN9Oqms5ML/Jolgryv3JW
# 7/yahp7lg/wn3PDnNwzC1BlFMyCdEktQmUXMmE4mmxSutRY1OGGagENWPjbQ1R/x
# 4HuTByWbG+TUV7dIPxE7TMy9Npco4f09DN6QFRXdk7bZTu58kxu6MFH6mzQkC3IX
# vExfo0c/CT4/hTZD2XknHjXLzi9jzInSHS9GQqeXd6nWbkwEu7znKpNEcSElorDS
# EcRhc1OSOLjUK0x0cOw8whzQ0fWGkPoqgCslbjZoko7U7kj/Fn4TWQ8s75LHjhVa
# o+w2SWMP0ELWYIkq1ComGq3wqBD7VU6rtCDoUf237HkgRBxLXFjwp444oKWnchYM
# Z7epWX8jqntSr4QHeidL72c6XswQIIZsFglV4BO28jO02yS7w4iyY0c6PQs34GZ7
# SWphsN1DaiX9ysc6hovVYoVA94t+3p4TI1RtK9Ayh0atmfYPJqtBfk5kJ1h4nNax
# 0lZmJL9chJjZQb+7XW8MQgstiQdsegaz2QL6Edm+94N8IW6TYaalyO9LeR35A9Mo
# TLo79rdB04naTqWdPTBKh7DpS53ZAQn5dYs2per+m7ZeIxnEWaNcG+PbRh+ZslFb
# 6M75JgN43szB+/VYVQIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
# HQ4EFgQUrFfKCBbcP8UxHApN2/vx3pknLTQwHwYDVR0jBBgwFoAUtqFUOQLDoD+O
# irz61PgcptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMD
# MDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5j
# cmwwbAYIKwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2Nz
# cC1jZXJ0dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0
# dW0ucGwvY3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcC
# ARYYaHR0cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC7
# khlZjFbU+gsRdcraFFJKj1PL63MVudnm/AILuNaxm7LIZOYtrZaRWEpR0qd3uhSp
# UEI29fMBZvuu3CHXJsM58hbJFo4YoHnyDJgTkeVmc10fd1NI//cI3nRD1/Alx09y
# WhPZlze0A/Bvwc3LcZtTywfsL5tD3QAL8ZqelrHt1Eb8AnDEnAzKNbm1mwYmD/5d
# y1XkIKfsM1pmRM2ZDcJ4qpwVewE7g+4e+Y+iq42LuaHPvNAkwq0daA9Hp3qemk0c
# RFfM5257MvEsu+6w8+44mpQUOgpASVIjY3l2vdf6BUfBJfsd3pcp/2hfDnXaql9A
# oB7ps7eCZb9VXKm/u2lTRMf5esOcZFy4udsnGBaOwSSUe0chYusfJ9IjsmpoMl4C
# W5E0R/U55Bz6TpDo+gaXQJL4i/zv0UR5papGsDguBASp9g/OfnvGTeWrS9l5orEk
# tu9ExrFfPvAKxZK+4AO9bapPhMATraKo6kx6K51pS1ZO2pf1r3B+lXN5bTLgp8g/
# dygUZm2cmauBAzJA5v/iHOb0b4b4Muqc8dxgcX1jZRLtLvxnAWxkKIMfj7A4M0Jr
# N4rLBUpPFM0+Bp2sR7rtFqNZW56huaDkxO75eNRN2z4A959R5GBRnFWtgE65+B1R
# HAQQ/TOF4mU0Y3oIhVQr3rUh4tdSvIxr8QpsDKIvsDCCBu0wggTVoAMCAQICEAqA
# 7xhLjfEFgtHEdqeVdGgwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVk
# IEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2
# MDQwMDAwMDBaFw0zNjA5MDMyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQw
# OTYgVGltZXN0YW1wIFJlc3BvbmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQDQRqwtEsae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ
# 3xTWcfsLwOvRxUwXcGx8AUjni6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqV
# Q+3bzWYesFtkepErvUSbf+EIYLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjo
# T1FpS54dNApZfKY61HAldytxNM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R
# 0V3Zp3DjjANwqAf4lEkTlCDQ0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6Un
# bksIcFJqLbkIXIPbcNmA98Oskkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39
# iTTFS+ENTqW8m6THuOmHHjQNC3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0
# dVVZw7knh1WZXOLHgDvundrAtuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6ll
# N3QgshRta6Eq4B40h5avMcpi54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wg
# gn8O2klETsJ7u8xEehGifgJYi+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmS
# F3voIgMFtNGh86w3ISHNm0IaadCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwID
# AQABo4IBlTCCAZEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85Fx
# YxlQQ89hjOgwHwYDVR0jBBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0P
# AQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSB
# iDCBhTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsG
# AQUFBzAChlFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0f
# BFgwVjBUoFKgUIZOaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1Ud
# IAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEA
# ZSqt8RwnBLmuYEHs0QhEnmNAciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZ
# hY+hIfP2JkQ38U+wtJPBVBajYfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/
# ciZmUnthfAEP1HShTrY+2DE5qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk
# 6FxRPyUPxAAYH2Vy1lNM4kzekd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQ
# USntbjZ80FU3i54tpx5F/0Kr15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdC
# G1+lqvP4FbrQ6IwSBXkZagHLhFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y5
# 8678IgmfORBPC1JKkYaEt2OdDh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V0
# 8X1JUPvB4ILfJdmL+66Gp3CSBXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1q
# mcwbdUfcSYCn+OwncVUXf53VJUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfY
# xJ7La54i71McVWRP66bW+yERNpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhD
# Bf3Froguzzhk++ami+r3Qrx5bIbY3TVzgiFI7Gq3zWcxggXaMIIF1gIBATB+MGox
# CzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4x
# ODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9uIENvZGUgU2lnbmlu
# ZyAyMDIxIENBAhAfsijAKebq6quDMIHCRvIfMA0GCWCGSAFlAwQCAQUAoIGEMBgG
# CisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIE
# IHImzOLyBOo2pnkghZ4PB3uMclLzRBTKmxtKoaH8TOsyMA0GCSqGSIb3DQEBAQUA
# BIIBgLPJA4Ya2KwpeE/jrlQfYhWrKIpQtRm481Cmm05VunrF8LHG1zPTSJbOe4PU
# fPrxVM1PTEi+f4tm5w4xMinbrIUAJD7TtPQbued88HZe8ssK42qlDpeWZ5iDwpS3
# djxEFLmVf2Xg5AQ59fBpmti3EVyOUaFK2VszH1VizYNpZXvfL9jvO7O8cqGY/aRW
# il7CqV4ywdUD7QsAU8HCvy1ucc0t2HQqo8AFRofSLtZ9pDwxqNnOUwzJnD2hwldQ
# Jll8LO2LtPmocS5tu0aC+INvN0mAkw1G+gr/Bp3UB6jjhRRFrbd/zidM5PAuHn3L
# sp5WhR5UkkpSxeqb7uvn0Q8DUfZDU6lFppNMX5g+I/ZWA9UAfb93g+zdu0IuFQeF
# Qg5aDjjJeGIyzhxMA4N36eEhXnHiDE853uWDhxEkO4jFK0DqdKFbsWQVVGd6mVvL
# 8/ZVjm+SPYqt0jud1NhGOLweR6nGnfhRsw7KK9GWp6EJf7So9xt1Pm9nrJtAOc4V
# WYEwb6GCAyYwggMiBgkqhkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYT
# AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQg
# VHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEC
# EAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMx
# CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjA0MTQwNzE0MzdaMC8GCSqG
# SIb3DQEJBDEiBCBMwyXAuv6dKnYIcChcdIH+f96Q7HTguuZuGnbHX6zslTANBgkq
# hkiG9w0BAQEFAASCAgBNnvcVf8gt6VX1NS07zXBT7uov6JLCeee8qsuEholrNmxY
# gXpIKL7fyL3cLrTmbV6NSciLnVLSNVn2b9zhzBzBYoUFU8gi4zzVqoWTEEpRxTHm
# yEP1M9yvuDQc1NkF/dCwKN1UrOMUot1KPmJWraNuORXRgiz5IPur5WiZ1JnXdcIE
# 8bXo90qdLTnU3gpOupnbxwUzGgwC6yEqe7qIgVl+l0qhx9WmCQZHAOkPMIFEQ8vD
# 3BnSWQMFnFkvPWwRyvtzBLy0eVbibEaQUeazwsljvO1xosbA2TiSh8HmsOfU2weS
# 6yqhVfNjiAK7Mav5dzZBRbVo00dQOrm7L0X3VSh3QMU9IUJ173GkaGiz++qQqXup
# 5H83vz75cdAM8KI3kvWwnHIK2op1Jj65vP73fPCh4pQyYbp70trQMmlYY6KKAkvf
# Ls7hM624DAPfQvrnf/TXfXgQJhI94kP5XKF4Hxm5+JZeUUdcWT0VWRLgjIY4KThK
# 5s9rlUB97J8NZAjhbnYdXx4tJ/5M9yPIJ2T8bstjzmfPMHTBw93athl2m0GOQvVR
# /4mWWyGR+7o8/uC4fjiJjc7OPfrfTa/Vn3PbClxfINtT5FfG8vWp5cLaal5X84L4
# zBUXisG8INaBYnDANoSfseVvZmGv+GlHPUXErExYOBixQs32PseWm1j5r79GXQ==
# SIG # End signature block