From 63c8b27b89b8b4b9f1804deaf2e111dde185a77d Mon Sep 17 00:00:00 2001 From: Stephan Yelle Date: Thu, 22 May 2025 00:23:13 -0400 Subject: [PATCH] visual and effects are working attempting to recreate a cleaner version of the script --- testTaskGate.ps1 | 576 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 463 insertions(+), 113 deletions(-) diff --git a/testTaskGate.ps1 b/testTaskGate.ps1 index 443c976..5f334b0 100644 --- a/testTaskGate.ps1 +++ b/testTaskGate.ps1 @@ -1,147 +1,497 @@ -# Sample functional PowerShell script using data-driven checkbox definitions -# Single-file, dynamic UI & routing based on a single $Global:Tasks table +#region Config & Task Definitions -#region Config -$Port = 8081 -#endregion - -#region Task Definitions -# Each task: Id → checkbox HTML id -# Name → route name & JS handler path -# Label → displayed text -# HandlerFn → PowerShell function to invoke +# Listening port for HTTP UI +$Port = 8081 +# Define every task once here: +# Id → checkbox HTML `id` +# Name → URL path (`/Name`) +# Label → user-visible text +# HandlerFn → the PowerShell function to invoke +# Page → which tab/page it appears on $Global:Tasks = @( - @{ Id = 'setSVSPowerplan'; Name = 'setSVSPowerplan'; Label = 'Set SVS Powerplan'; HandlerFn = 'Set-SVSPowerPlan' }, - @{ Id = 'installSVSMSPModule'; Name = 'installSVSMSPModule'; Label = 'Install SVSMSP Module'; HandlerFn = 'Install-SVSMSPModule' }, - @{ Id = 'installCyberQP'; Name = 'installCyberQP'; Label = 'Install CyberQP'; HandlerFn = 'Install-CyberQP' } - # ...add more tasks here + # On-Boarding + @{ Id='setSVSPowerplan'; Name='setSVSPowerplan'; Label='Set SVS Powerplan'; HandlerFn='Set-SVSPowerPlan'; Page='onboard' }, + @{ Id='installSVSMSPModule'; Name='installSVSMSPModule'; Label='Install SVSMSP Module'; HandlerFn='Install-SVSMSPModule'; Page='onboard' }, + @{ Id='installCyberQP'; Name='installCyberQP'; Label='Install CyberQP'; HandlerFn='Install-CyberQP'; Page='onboard' }, + + # Off-Boarding + @{ Id='uninstallCyberQP'; Name='uninstallCyberQP'; Label='Uninstall CyberQP'; HandlerFn='Uninstall-CyberQP'; Page='offboard' }, + @{ Id='uninstallSVSMSPModule';Name='uninstallSVSMSPModule';Label='Uninstall SVSMSP Module'; HandlerFn='Cleanup-SVSMSP'; Page='offboard' }, + + # Tweaks + @{ Id='disableAnimations'; Name='disableAnimations'; Label='Disable Animations'; HandlerFn='Disable-Animations'; Page='tweaks' }, + + # SVS Apps + @{ Id='wingetLastpass'; Name='wingetLastpass'; Label='LastPass Desktop App'; HandlerFn='Install-WingetLastPass'; Page='SVSApps' } ) + #endregion -#region Handler Function Stubs -function Set-SVSPowerPlan { - param($Context) - Write-LogHybrid -Message "[Handler] PowerPlan set" -Level Success - if ($Context) { Respond-Text $Context "Powerplan applied" } +#region Logging Helpers + +# Initialize a global in-memory log cache +if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) { + $Global:LogCache = [System.Collections.ArrayList]::new() } -function Install-SVSMSPModule { - param($Context) - Write-LogHybrid -Message "[Handler] SVSMSP Module installed" -Level Success - if ($Context) { Respond-Text $Context "SVSMSP Module installed" } -} +# Core Write-Log function (advanced with event-log support) +function Write-LogHelper { + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$Message, + [ValidateSet("Info","Warning","Error","Success","General")] + [string]$Level = "Info", + [string]$TaskCategory = "GeneralTask", + [switch]$LogToEvent, + [string]$EventSource = "SVSMSP_Module", + [string]$EventLog = "Application", + [int]$CustomEventID + ) + $EventID = @{ Info=1000; Warning=2000; Error=3000; Success=4000; General=1000 }[$Level] + $Icon = @{ Info="📝"; Warning="⚠️"; Error="❌"; Success="✅"; General="📦" }[$Level] + $logEntry = [PSCustomObject]@{ + Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") + Level = $Level + Message = "$Icon [$Level] [$TaskCategory] $Message (EventID:$EventID)" + } + [void]$Global:LogCache.Add($logEntry) -function Install-CyberQP { - param($Context) - Write-LogHybrid -Message "[Handler] CyberQP installed" -Level Success - if ($Context) { Respond-Text $Context "CyberQP installed" } -} - -# Utility for sending plain-text HTTP responses -function Respond-Text { - param($Context, $Text) - $buffer = [Text.Encoding]::UTF8.GetBytes($Text) - $Context.Response.ContentType = 'text/plain' - $Context.Response.ContentLength64 = $buffer.Length - $Context.Response.OutputStream.Write($buffer, 0, $buffer.Length) - $Context.Response.OutputStream.Close() -} -#endregion - -#region UI Generation -function Get-UIHtml { - # Build checkbox HTML (wrap pipeline in parentheses so -join applies to result array) - $checkboxes = ( - $Global:Tasks | ForEach-Object { - "" - } - ) -join "`n" - - # Build JS tasks array - $tasksJs = ( - $Global:Tasks | ForEach-Object { - "{ id: '$($_.Id)', handler: '/$($_.Name)' }" - } - ) -join ",`n " - - $html = @" - - -Dynamic UI - -

Task Launcher

-
-$checkboxes -
- -

-
-
-
-"@
-    return $html
+
+# Hybrid wrapper: uses your module's Write-Log if available, else falls back
+if (Get-Command Write-Log -ErrorAction SilentlyContinue) {
+    function Write-LogHybrid { param($Message,$Level,$TaskCategory,$LogToEvent) Write-Log @PSBoundParameters }
+} else {
+    function Write-LogHybrid { param($Message,$Level,$TaskCategory,$LogToEvent) Write-LogHelper @PSBoundParameters }
 }
+
 #endregion
 
-#region HTTP Server & Routing
+#region Handler Stubs
+
+function Respond-Text {
+    param($Context, $Text)
+    $bytes = [Text.Encoding]::UTF8.GetBytes($Text)
+    $Context.Response.ContentType     = 'text/plain'
+    $Context.Response.ContentLength64 = $bytes.Length
+    $Context.Response.OutputStream.Write($bytes,0,$bytes.Length)
+    $Context.Response.OutputStream.Close()
+}
+
+function Respond-HTML {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory = $true)][object] $Context,
+        [Parameter(Mandatory = $true)][string] $Html
+    )
+    $bytes = [Text.Encoding]::UTF8.GetBytes($Html)
+    $Context.Response.ContentType     = 'text/html'
+    $Context.Response.ContentLength64 = $bytes.Length
+    $Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
+    $Context.Response.OutputStream.Close()
+}
+
+
+# On-boarding handlers
+function Set-SVSPowerPlan {
+    param($Context)
+    Write-LogHybrid "PowerPlan set" "Success" "OnBoard"
+    Respond-Text $Context "Powerplan applied"
+}
+function Install-SVSMSPModule {
+    param($Context)
+    Write-LogHybrid "SVSMSP Module installed" "Success" "OnBoard"
+    Respond-Text $Context "SVSMSP Module installed"
+}
+function Install-CyberQP {
+    param($Context)
+    Write-LogHybrid "CyberQP installed" "Success" "OnBoard"
+    Respond-Text $Context "CyberQP installed"
+}
+
+# Off-boarding handlers
+function Uninstall-CyberQP {
+    param($Context)
+    Write-LogHybrid "CyberQP uninstalled" "Success" "OffBoard"
+    Respond-Text $Context "CyberQP uninstalled"
+}
+function Cleanup-SVSMSP {
+    param($Context)
+    Write-LogHybrid "SVSMSP cleaned up" "Success" "OffBoard"
+    Respond-Text $Context "SVSMSP cleaned up"
+}
+
+# Tweaks handler
+function Disable-Animations {
+    param($Context)
+    Write-LogHybrid "Animations disabled" "Success" "Tweaks"
+    Respond-Text $Context "Animations disabled"
+}
+
+# SVSApps handler
+function Install-WingetLastPass {
+    param($Context)
+    Write-LogHybrid "Winget LastPass installed" "Success" "SVSApps"
+    Respond-Text $Context "Winget LastPass installed"
+}
+
+#endregion
+
+#region UI Generation
+
+function Get-UIHtml {
+    param([string]$Page = 'onboard')
+
+    #
+    # 1) Inline your full original CSS here
+    #
+$style = @'
+
+'@
+
+    $script = @'
+    
+
+'@
+
+    #
+    # 3) The HTML skeleton with placeholders
+    #
+$htmlTemplate = @"
+
+
+
+  
+  
+  SVS TaskGate
+  
+  $style
+
+
+  
+ SVS Logo +
+
+ +
+
+

On-Boarding

+
+{{onboardCheckboxes}} +
+ +
+
+

Off-Boarding

+
+{{offboardCheckboxes}} +
+ +
+
+

Tweaks

+
+{{tweaksCheckboxes}} +
+ +
+
+

SVS APPs

+
+{{appsCheckboxes}} +
+ +
+
+
+ $script + +
+ + +
+ + + +"@ + + # + # 4) Build the checkbox HTML and tasks JS from $Global:Tasks + # + $build = { + param($page) + ($Global:Tasks | Where Page -EQ $page | ForEach-Object { + "" + }) -join "`n" + } + $onboard = & $build 'onboard' + $offboard = & $build 'offboard' + $tweaks = & $build 'tweaks' + $apps = & $build 'SVSApps' + + $tasksJsAll = ($Global:Tasks | ForEach-Object { + " { id: '$($_.Id)', handler: '/$($_.Name)', label: '$($_.Label)' }" + }) -join ",`n" + + # + # 5) Inject into template + # + $html = $htmlTemplate + $html = $html.Replace('{{onboardCheckboxes}}', $onboard) + $html = $html.Replace('{{offboardCheckboxes}}', $offboard) + $html = $html.Replace('{{tweaksCheckboxes}}', $tweaks) + $html = $html.Replace('{{appsCheckboxes}}', $apps) + $html = $html.Replace('{{tasksJsAll}}', $tasksJsAll) + $html = $html.Replace('{{defaultPage}}', $Page) + + return $html +} + +#endregion + + + + +#region HTTP Listener & Routing + +# Handle shutdown command +if ($path -eq 'quit') { + Write-LogHybrid "Shutdown requested" "Info" "Server" + Respond-Text $Context "Server shutting down." + # This will break out of the while loop in Start-Server + $Global:Listener.Stop() + return +} + +# Sends the HTML for a given page or invokes a task handler function Dispatch-Request { param($Context) + + # figure out the path $path = $Context.Request.Url.AbsolutePath.TrimStart('/') - # Match route to a task + + # ---- Shutdown handler ---- + if ($path -eq 'quit') { + Write-LogHybrid "Shutdown requested" "Info" "Server" + Respond-Text $Context "Server shutting down." + # stop the listener loop + $Global:Listener.Stop() + return + } + + # ---- Serve UI pages ---- + if ($path -in @('', 'onboard', 'offboard', 'tweaks', 'SVSApps')) { + $page = if ($path -eq '') { 'onboard' } else { $path } + $html = Get-UIHtml -Page $page + Respond-HTML $Context $html + return + } + + # ---- Task invocation ---- $task = $Global:Tasks | Where-Object Name -EQ $path if ($task) { & $task.HandlerFn $Context return } - # Serve UI - if ($path -eq '' -or $path -eq '/') { - $html = Get-UIHtml - $buffer = [Text.Encoding]::UTF8.GetBytes($html) - $Context.Response.ContentType = 'text/html' - $Context.Response.ContentLength64 = $buffer.Length - $Context.Response.OutputStream.Write($buffer, 0, $buffer.Length) - $Context.Response.OutputStream.Close() - return - } - # Not found + + # ---- 404 ---- $Context.Response.StatusCode = 404 Respond-Text $Context '404 - Not Found' } + +# Starts the HTTP listener loop function Start-Server { - $listener = New-Object System.Net.HttpListener - $listener.Prefixes.Add("http://localhost:$Port/") - $listener.Start() - Write-Host "Listening on http://localhost:$Port/" - while ($listener.IsListening) { - $ctx = $listener.GetContext() - try { Dispatch-Request $ctx } catch { Write-Host "Error:" $_ } + # make it accessible to Dispatch-Request + $Global:Listener = [System.Net.HttpListener]::new() + $Global:Listener.Prefixes.Add("http://localhost:$Port/") + $Global:Listener.Start() + Write-Host "Listening on http://localhost:$Port/ ..." + + try { + while ($Global:Listener.IsListening) { + $ctx = $Global:Listener.GetContext() + try { + Dispatch-Request $ctx + } catch { + Write-LogHybrid "Dispatch error: $_" "Error" "Server" + } + } + } finally { + # once the loop exits, clean up + $Global:Listener.Close() + Write-LogHybrid "Listener closed." "Info" "Server" } - $listener.Stop() } + + #endregion -# Bootstrap +# Bootstrap: launch the server Start-Server