diff --git a/testTaskGate.ps1 b/testTaskGate.ps1 index 0035b32..f816d81 100644 --- a/testTaskGate.ps1 +++ b/testTaskGate.ps1 @@ -1,172 +1,142 @@ -# SVS TaskGate: Self-contained PowerShell Task Runner UI (All-in-One Script) +# Sample functional PowerShell script using data-driven checkbox definitions +# Single-file, dynamic UI & routing based on a single $Global:Tasks table -# ----- 1. TASK REGISTRY ----- -$TaskRegistry = @{ - Onboarding = @( - @{ Key = "installSVSMSP"; Label = "Install SVSMSP Module"; Function = "Install-SVSMSP" } - @{ Key = "installDattoRMM"; Label = "Install DattoRMM"; Function = "Install-DattoRMM" } - @{ Key = "installCyberQP"; Label = "Install CyberQP"; Function = "Install-CyberQP" } - ) - Offboarding = @( - @{ Key = "uninstallSVSMSP"; Label = "Uninstall SVSMSP Module"; Function = "Uninstall-SVSMSP" } - @{ Key = "uninstallDattoRMM"; Label = "Uninstall DattoRMM"; Function = "Uninstall-DattoRMM" } - @{ Key = "uninstallCyberQP"; Label = "Uninstall CyberQP"; Function = "Uninstall-CyberQP" } - ) - Tweaks = @( - @{ Key = "setPowerPlan"; Label = "Set Power Plan"; Function = "Set-SVSPowerPlan" } - @{ Key = "enableBitLocker"; Label = "Enable BitLocker"; Function = "Enable-BitLocker" } - ) +#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 + +$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 +) +#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" } } -# ----- 2. FUNCTION STUBS (Replace with real logic) ----- -function Install-SVSMSP { Write-Host "Installing SVSMSP Module..." } -function Install-DattoRMM { Write-Host "Installing DattoRMM..." } -function Install-CyberQP { Write-Host "Installing CyberQP..." } -function Uninstall-SVSMSP { Write-Host "Uninstalling SVSMSP Module..." } -function Uninstall-DattoRMM { Write-Host "Uninstalling DattoRMM..." } -function Uninstall-CyberQP { Write-Host "Uninstalling CyberQP..." } -function Set-SVSPowerPlan { Write-Host "Setting SVS Power Plan..." } -function Enable-BitLocker { Write-Host "Enabling BitLocker..." } - -# ----- 3. HTML UI GENERATOR ----- -function Get-TasksHtml($group) { - $html = "" - foreach ($task in $TaskRegistry[$group]) { - $html += "
`n" - } - return $html +function Install-SVSMSPModule { + param($Context) + Write-LogHybrid -Message "[Handler] SVSMSP Module installed" -Level Success + if ($Context) { Respond-Text $Context "SVSMSP Module installed" } } -$HtmlContent = @" +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 + $checkboxes = $Global:Tasks | ForEach-Object { + "" + } -join "`n" + + # Build JS tasks array + $tasksJs = $Global:Tasks | ForEach-Object { + "{ id: '$($_.Id)', handler: '/$($_.Name)' }" + } -join ",`n " + + $html = @" - - - SVS TaskGate - - +Dynamic UI -

SVS TaskGate

-
-

Onboarding

-
- $(Get-TasksHtml 'Onboarding') -
- -
-
-

Offboarding

-
- $(Get-TasksHtml 'Offboarding') -
- -
-
-

Tweaks

-
- $(Get-TasksHtml 'Tweaks') -
- -
-
- +} + "@ - -# ----- 4. HTTP LISTENER (Self-contained, single-file, no dependencies) ----- -Add-Type -AssemblyName System.Net.HttpListener - -$listener = New-Object System.Net.HttpListener -$listener.Prefixes.Add("http://localhost:8081/") -$listener.Start() -Write-Host "TaskGate listening at http://localhost:8081/" - -# Open browser to UI (Edge, fallback Chrome) -try { - Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/" -} catch { - Start-Process "chrome.exe" "http://localhost:8081/" + return $html } +#endregion -while ($listener.IsListening) { - $context = $listener.GetContext() - $req = $context.Request - $res = $context.Response - - try { - switch ($req.Url.AbsolutePath) { - "/" { - $bytes = [System.Text.Encoding]::UTF8.GetBytes($HtmlContent) - $res.ContentType = "text/html" - $res.ContentLength64 = $bytes.Length - $res.OutputStream.Write($bytes, 0, $bytes.Length) - $res.OutputStream.Close() - } - "/run-tasks" { - if ($req.HttpMethod -ne "POST") { - $res.StatusCode = 405 - $res.OutputStream.Close() - continue - } - $reader = New-Object IO.StreamReader $req.InputStream - $body = $reader.ReadToEnd() | ConvertFrom-Json - $group = $body.group - $selectedKeys = $body.tasks - $executed = @() - foreach ($key in $selectedKeys) { - $task = $TaskRegistry[$group] | Where-Object { $_.Key -eq $key } - if ($task) { - try { - & $task.Function - $executed += $task.Label - } catch { - $executed += "$($task.Label) (ERROR)" - } - } - } - $resp = @{ status = "OK"; executed = $executed } - $bytes = [System.Text.Encoding]::UTF8.GetBytes(($resp | ConvertTo-Json)) - $res.ContentType = "application/json" - $res.ContentLength64 = $bytes.Length - $res.OutputStream.Write($bytes, 0, $bytes.Length) - $res.OutputStream.Close() - } - default { - $res.StatusCode = 404 - $bytes = [System.Text.Encoding]::UTF8.GetBytes("404 Not Found") - $res.OutputStream.Write($bytes, 0, $bytes.Length) - $res.OutputStream.Close() - } - } - } catch { - $res.StatusCode = 500 - $err = "Error: $($_.Exception.Message)" - $bytes = [System.Text.Encoding]::UTF8.GetBytes($err) - $res.OutputStream.Write($bytes, 0, $bytes.Length) - $res.OutputStream.Close() +#region HTTP Server & Routing +function Dispatch-Request { + param($Context) + $path = $Context.Request.Url.AbsolutePath.TrimStart('/') + # Match route to a task + $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 + $Context.Response.StatusCode = 404 + Respond-Text $Context '404 - Not Found' } + +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:" $_ } + } + $listener.Stop() +} +#endregion + +# Bootstrap +Start-Server