Public/Start-LMSessionSyncServer.ps1

<#
.SYNOPSIS
    Starts a session synchronization server for Logic Monitor.
.DESCRIPTION
    The Start-LMSessionSyncServer function starts a Pode server that synchronizes sessions for Logic Monitor.
    It manages a secret vault for session storage and provides logging options.
.PARAMETER EnableRequestLogging
    Enables request logging when specified.
.PARAMETER EnableErrorLogging
    Enables error logging when specified.
.EXAMPLE
    Start-LMSessionSyncServer -EnableRequestLogging -EnableErrorLogging
#>

Function Start-LMSessionSyncServer {
    [CmdletBinding()]
    Param(
        [Switch]$EnableRequestLogging,
        [Switch]$EnableErrorLogging
    )
    
    Begin {
        $Config = @{
            VaultName      = "Logic.Monitor"
            VaultKeyPrefix = "LMSessionSync"
            ServerConfig   = @{
                Address  = '127.0.0.1'
                Port     = 8072
                Protocol = 'Http'
            }
            Security       = @{
                Origin   = '*'
                Methods  = '*'
                Headers  = '*'
                Duration = 7200
            }
        }

        Initialize-SecretVault -VaultName $Config.VaultName
    }
    
    Process {
        Start-PodeServer {
            # Initialize vault and server state
            if (-not (Initialize-VaultAccess -VaultName $Config.VaultName)) {
                return
            }

            $apiKey = Initialize-ApiKey -VaultName $Config.VaultName -VaultKeyPrefix $Config.VaultKeyPrefix

            # Set server state
            @('VaultName', 'VaultKeyPrefix', 'VaultApiKey') | ForEach-Object {
                Set-PodeState -Name $_ -Value $(
                    switch ($_) {
                        'VaultName' { $Config.VaultName }
                        'VaultKeyPrefix' { $Config.VaultKeyPrefix }
                        'VaultApiKey' { $apiKey }
                    }
                ) | Out-Null
            }

            # Configure server
            Add-PodeEndpoint -Address $Config.ServerConfig.Address -Port $Config.ServerConfig.Port -Protocol $Config.ServerConfig.Protocol
            Set-PodeSecurityAccessControl -Origin $Config.Security.Origin -Methods $Config.Security.Methods -Headers $Config.Security.Headers -Duration $Config.Security.Duration

            # Configure logging
            if ($EnableRequestLogging) { New-PodeLoggingMethod -Terminal | Enable-PodeRequestLogging }
            if ($EnableErrorLogging) { New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging }

            # Authentication
            New-PodeAuthScheme -ApiKey | Add-PodeAuth -Name 'Auth' -Sessionless -ScriptBlock {
                param($key)
                
                $apiKey = Get-PodeState -Name 'VaultApiKey'

                if ($key.toString() -eq $apiKey.toString()) {
                    return @{ User = @{ ID = '1' } }
                }
                elseif ($WebEvent.Request.Headers.Origin -match '^https://deploy\.lmdemo\.us|^http://localhost:') {
                    return @{ User = @{ ID = '2' } }
                }
                
                return $null
            }

            # Routes
            Add-PodeRoute -Method Get -Path '/api/v1/portal/:AccountName' -Authentication 'Auth' -ScriptBlock {
                Try {
                    $vaultName = Get-PodeState -Name 'VaultName'
                    $vaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                    $accountName = $WebEvent.Parameters['AccountName']
                    
                    Unlock-SecretVault -Name $vaultName -Password (Get-PodeState -Name 'VaultUnlock')
                    $secretData = Get-Secret -Name "$vaultKeyPrefix-$accountName" -Vault $vaultName -AsPlainText
                    Write-PodeJsonResponse -Value $secretData
                }
                Catch {
                    Write-PodeTextResponse -Value $_.Exception.Message -StatusCode 500
                }
            }

            # Health check routes
            @('/api/v1/portal', '/api/v1/health') | ForEach-Object {
                Add-PodeRoute -Method Options -Path $_ -ScriptBlock {
                    Write-PodeTextResponse -Value 'OK' -StatusCode 200
                }
            }
            
            Add-PodeRoute -Method Get -Path '/api/v1/health' -ScriptBlock {
                Write-PodeTextResponse -Value 'OK' -StatusCode 200
            }

            Add-PodeRoute -Method Get -Path '/api/v1/portal' -Authentication 'Auth' -ScriptBlock {
                Try {
                    $vaultName = Get-PodeState -Name 'VaultName'
                    $vaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                    
                    Unlock-SecretVault -Name $vaultName -Password (Get-PodeState -Name 'VaultUnlock')
                    $sessions = Get-SecretInfo -Vault $vaultName | 
                        Where-Object { $_.Name -like "$vaultKeyPrefix*" -and $_.Name -notlike "*RESTAPIKey" }
                    
                    $secretData = $sessions | ForEach-Object {
                        Try {
                            $secret = Get-Secret -Vault $vaultName -Name $_.Name -AsPlainText -ErrorAction Stop
                            [PSCustomObject]@{
                                Portal   = $_.Metadata["Portal"]
                                Modified = $_.Metadata["Modified"]
                                Type     = $_.Metadata["Type"]
                                Secret   = $secret | ConvertFrom-Json
                            }
                        }
                        Catch {
                            Write-Error "Failed to process session: $($_.Metadata['Portal']): $_"
                            $null
                        }
                    } | Where-Object { $_ -ne $null }
                    
                    Write-PodeJsonResponse -Value $secretData
                }
                Catch {
                    Write-PodeTextResponse -Value $_.Exception.Message -StatusCode 500
                }
            }

            Add-PodeRoute -Method Post -Path '/api/v1/portal/:AccountName' -ScriptBlock {
                Try {
                    $vaultName = Get-PodeState -Name 'VaultName'
                    $vaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                    $accountName = $WebEvent.Parameters['AccountName']
                    $secretData = $WebEvent.Data | ConvertTo-Json
                    
                    Unlock-SecretVault -Name $vaultName -Password (Get-PodeState -Name 'VaultUnlock')
                    Set-Secret -Name "$vaultKeyPrefix-$accountName" -Vault $vaultName -Secret $secretData -Metadata @{
                        Modified = (Get-Date).ToString('o')
                        Type     = "SessionSync"
                        Portal   = $accountName
                    }
                    
                    Write-PodeJsonResponse -Value $secretData
                }
                Catch {
                    Write-PodeTextResponse -Value $_.Exception.Message -StatusCode 500
                }
            }

            Register-PodeEvent -Type Terminate -Name 'CleanupSessions' -ScriptBlock {
                Try {
                    $vaultName = Get-PodeState -Name 'VaultName'
                    $vaultKeyPrefix = Get-PodeState -Name 'VaultKeyPrefix'
                    
                    Unlock-SecretVault -Name $vaultName -Password (Get-PodeState -Name 'VaultUnlock')
                    $sessions = Get-SecretInfo -Vault $vaultName | 
                        Where-Object { $_.Name -like "*$vaultKeyPrefix*" }
                    
                    foreach ($session in $sessions) {
                        Try {
                            Remove-Secret -Vault $vaultName -Name $session.Name -ErrorAction Stop
                            Write-Host "Cleared session: $($session.Metadata['Portal'])" -ForegroundColor Green
                        }
                        Catch {
                            Write-Error "Failed to clear session: $($session.Metadata['Portal']): $_"
                        }
                    }
                }
                Finally {
                    Disconnect-LMAccount
                }
            }
        }
    }
    
    End {}
}