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
+
+
+
+

+
+
+
+
+
+
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