Update testTaskGate.ps1

This commit is contained in:
2025-05-21 17:22:32 -04:00
parent 001bf8a654
commit ef55468b90

View File

@@ -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 ----- #region Config
$TaskRegistry = @{ $Port = 8081
Onboarding = @( #endregion
@{ Key = "installSVSMSP"; Label = "Install SVSMSP Module"; Function = "Install-SVSMSP" }
@{ Key = "installDattoRMM"; Label = "Install DattoRMM"; Function = "Install-DattoRMM" } #region Task Definitions
@{ Key = "installCyberQP"; Label = "Install CyberQP"; Function = "Install-CyberQP" } # Each task: Id → checkbox HTML id
) # Name → route name & JS handler path
Offboarding = @( # Label → displayed text
@{ Key = "uninstallSVSMSP"; Label = "Uninstall SVSMSP Module"; Function = "Uninstall-SVSMSP" } # HandlerFn → PowerShell function to invoke
@{ Key = "uninstallDattoRMM"; Label = "Uninstall DattoRMM"; Function = "Uninstall-DattoRMM" }
@{ Key = "uninstallCyberQP"; Label = "Uninstall CyberQP"; Function = "Uninstall-CyberQP" } $Global:Tasks = @(
) @{ Id = 'setSVSPowerplan'; Name = 'setSVSPowerplan'; Label = 'Set SVS Powerplan'; HandlerFn = 'Set-SVSPowerPlan' },
Tweaks = @( @{ Id = 'installSVSMSPModule'; Name = 'installSVSMSPModule'; Label = 'Install SVSMSP Module'; HandlerFn = 'Install-SVSMSPModule' },
@{ Key = "setPowerPlan"; Label = "Set Power Plan"; Function = "Set-SVSPowerPlan" } @{ Id = 'installCyberQP'; Name = 'installCyberQP'; Label = 'Install CyberQP'; HandlerFn = 'Install-CyberQP' }
@{ Key = "enableBitLocker"; Label = "Enable BitLocker"; Function = "Enable-BitLocker" } # ...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-SVSMSPModule {
function Install-SVSMSP { Write-Host "Installing SVSMSP Module..." } param($Context)
function Install-DattoRMM { Write-Host "Installing DattoRMM..." } Write-LogHybrid -Message "[Handler] SVSMSP Module installed" -Level Success
function Install-CyberQP { Write-Host "Installing CyberQP..." } if ($Context) { Respond-Text $Context "SVSMSP Module installed" }
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 += "<label><input type='checkbox' value='$($task.Key)'> $($task.Label)</label><br>`n"
}
return $html
} }
$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 {
"<label><input type=`"checkbox`" id=`"$($_.Id)`" name=`"$($_.Name)`"> $($_.Label)</label>"
} -join "`n"
# Build JS tasks array
$tasksJs = $Global:Tasks | ForEach-Object {
"{ id: '$($_.Id)', handler: '/$($_.Name)' }"
} -join ",`n "
$html = @"
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head><meta charset="UTF-8"><title>Dynamic UI</title></head>
<meta charset="UTF-8">
<title>SVS TaskGate</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #181c1f; color: #fff; }
h2 { color: #33aaff; }
.section { margin-bottom: 32px; }
.btn { padding: 10px 24px; margin-top: 8px; background: #33aaff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
.btn:disabled { background: #444; }
#output { margin-top: 20px; color: #2ecc40; font-weight: bold; }
</style>
</head>
<body> <body>
<h1>SVS TaskGate</h1> <h1>Task Launcher</h1>
<div class="section"> <div class="checkbox-group">
<h2>Onboarding</h2> $checkboxes
<div id='onboarding-tasks'> </div>
$(Get-TasksHtml 'Onboarding') <button onclick="triggerInstall()">Run Selected</button>
</div> <pre id="logArea"></pre>
<button class="btn" onclick="runTasks('Onboarding')">Run Onboarding</button> <script>
</div> const tasks = [
<div class="section"> $tasksJs
<h2>Offboarding</h2> ];
<div id='offboarding-tasks'> async function triggerInstall() {
$(Get-TasksHtml 'Offboarding') const log = document.getElementById('logArea');
</div> log.textContent = '';
<button class="btn" onclick="runTasks('Offboarding')">Run Offboarding</button> for (const t of tasks) {
</div> const cb = document.getElementById(t.id);
<div class="section"> if (cb.checked) {
<h2>Tweaks</h2> log.textContent += `Calling ${t.id}...\n`;
<div id='tweaks-tasks'> try {
$(Get-TasksHtml 'Tweaks') const resp = await fetch(t.handler, { method: 'GET' });
</div> const txt = await resp.text();
<button class="btn" onclick="runTasks('Tweaks')">Run Tweaks</button> log.textContent += txt + "\n";
</div> } catch (e) {
<div id="output"></div> log.textContent += `Error ${t.id}: ${e.message}\n`;
<script> }
function runTasks(group) { }
const container = document.getElementById(group.toLowerCase() + '-tasks');
const checkboxes = container.querySelectorAll('input[type="checkbox"]:checked');
const selected = Array.from(checkboxes).map(cb => cb.value);
if(selected.length==0){alert('No tasks selected');return;}
fetch('/run-tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ group: group, tasks: selected })
})
.then(r=>r.json()).then(d=>{
document.getElementById('output').textContent = 'Executed: ' + d.executed.join(', ');
})
.catch(e => alert('Failed: ' + e));
} }
</script> }
</script>
</body> </body>
</html> </html>
"@ "@
return $html
# ----- 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/"
} }
#endregion
while ($listener.IsListening) { #region HTTP Server & Routing
$context = $listener.GetContext() function Dispatch-Request {
$req = $context.Request param($Context)
$res = $context.Response $path = $Context.Request.Url.AbsolutePath.TrimStart('/')
# Match route to a task
try { $task = $Global:Tasks | Where-Object Name -EQ $path
switch ($req.Url.AbsolutePath) { if ($task) {
"/" { & $task.HandlerFn $Context
$bytes = [System.Text.Encoding]::UTF8.GetBytes($HtmlContent) return
$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()
} }
# 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