Update testTaskGate.ps1
This commit is contained in:
276
testTaskGate.ps1
276
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 += "<label><input type='checkbox' value='$($task.Key)'> $($task.Label)</label><br>`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 {
|
||||
"<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>
|
||||
<html lang="en">
|
||||
<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>
|
||||
<head><meta charset="UTF-8"><title>Dynamic UI</title></head>
|
||||
<body>
|
||||
<h1>SVS TaskGate</h1>
|
||||
<div class="section">
|
||||
<h2>Onboarding</h2>
|
||||
<div id='onboarding-tasks'>
|
||||
$(Get-TasksHtml 'Onboarding')
|
||||
<h1>Task Launcher</h1>
|
||||
<div class="checkbox-group">
|
||||
$checkboxes
|
||||
</div>
|
||||
<button class="btn" onclick="runTasks('Onboarding')">Run Onboarding</button>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>Offboarding</h2>
|
||||
<div id='offboarding-tasks'>
|
||||
$(Get-TasksHtml 'Offboarding')
|
||||
</div>
|
||||
<button class="btn" onclick="runTasks('Offboarding')">Run Offboarding</button>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>Tweaks</h2>
|
||||
<div id='tweaks-tasks'>
|
||||
$(Get-TasksHtml 'Tweaks')
|
||||
</div>
|
||||
<button class="btn" onclick="runTasks('Tweaks')">Run Tweaks</button>
|
||||
</div>
|
||||
<div id="output"></div>
|
||||
<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));
|
||||
<button onclick="triggerInstall()">Run Selected</button>
|
||||
<pre id="logArea"></pre>
|
||||
<script>
|
||||
const tasks = [
|
||||
$tasksJs
|
||||
];
|
||||
async function triggerInstall() {
|
||||
const log = document.getElementById('logArea');
|
||||
log.textContent = '';
|
||||
for (const t of tasks) {
|
||||
const cb = document.getElementById(t.id);
|
||||
if (cb.checked) {
|
||||
log.textContent += `Calling ${t.id}...\n`;
|
||||
try {
|
||||
const resp = await fetch(t.handler, { method: 'GET' });
|
||||
const txt = await resp.text();
|
||||
log.textContent += txt + "\n";
|
||||
} catch (e) {
|
||||
log.textContent += `Error ${t.id}: ${e.message}\n`;
|
||||
}
|
||||
</script>
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</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/"
|
||||
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 }
|
||||
#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) {
|
||||
try {
|
||||
& $task.Function
|
||||
$executed += $task.Label
|
||||
} catch {
|
||||
$executed += "$($task.Label) (ERROR)"
|
||||
& $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
|
||||
}
|
||||
}
|
||||
$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()
|
||||
}
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user