This commit is contained in:
2025-05-17 14:45:45 -04:00
parent 23e414a3fc
commit 5a1a0fadf7

View File

@@ -1,310 +1,111 @@
### To Modify as of January 27 2025 ###-----------------------------------------------------------------------------###
### SVS TaskGate Launcher
###-----------------------------------------------------------------------------###
### let's start thinking about the write-log -TaskCategory "On-boarding" or "Off-boarding" # 1) GLOBAL LOG CACHE
### need RGB color codes from john, once we picked the RGBA colors
### add the .NET silent install tweaks to toolkit
### for the reg tweak need to do/undo function maybe it should have its own check box list
### added offboard check boxes for DattoRMM, DattoDEB, RocketCyber, CyberQP, SVSHelpdesk and Splashtop
### need to fix path in the uninstall-DattoEDR -
####### ❌ [Error] [GeneralTask] Uninstallation command 'C:\Program Files\Infocyte\Agent\agent.exe' not found. (Event ID: 3000) - bad path
# ---------------------------------------------------------------------------
# 1) CREATE A GLOBAL LOG CACHE (NEW)
# ---------------------------------------------------------------------------
if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) { if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) {
$Global:LogCache = New-Object System.Collections.ArrayList $Global:LogCache = New-Object System.Collections.ArrayList
} }
#region Write-LogHelper #region Write-LogHybrid
if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) { if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
function Write-LogHelper { function Write-LogHelper {
param ( param(
[string]$Message, [string]$Message,
[ValidateSet("Info", "Warning", "Error", "Success", "General")] [ValidateSet("Info","Warning","Error","Success","General")]
[string]$Level = "Info", [string]$Level = "Info",
[string]$TaskCategory = "GeneralTask", [string]$TaskCategory = "GeneralTask",
[switch]$LogToEvent = $false, [switch]$LogToEvent,
[string]$EventSource = "SVSMSP_Module", [string]$EventSource = "SVSMSP_Module",
[string]$EventLog = "Application", [string]$EventLog = "Application"
[int]$CustomEventID
) )
$EventID = switch ($Level) { $EventID = switch($Level){
"Info" { 1000 } "Info" {1000}; "Warning"{2000}; "Error"{3000}; "Success"{4000}; default{1000}
"Warning" { 2000 }
"Error" { 3000 }
"Success" { 4000 }
"General" { 1000 }
} }
$Icon = switch ($Level) { $Icon = switch($Level){
"Info" { [System.Char]::ConvertFromUtf32(0x1F4CB) } "Info" { [char]0x1F4CB }
"Warning" { ([char]0x26A0) } "Warning" { [char]0x26A0 }
"Error" { ([char]0x274C) } "Error" { [char]0x274C }
"Success" { ([char]0x2705) } "Success" { [char]0x2705 }
"General" { ([char]0x1F4E6) } default { [char]0x1F4E6 }
} }
$logEntry = [PSCustomObject]@{ $entry = [PSCustomObject]@{
Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
Level = $Level Level = $Level
Message = "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)" Message = "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)"
} }
[void]$Global:LogCache.Add($logEntry) [void]$Global:LogCache.Add($entry)
if ($LogToEvent) { if ($LogToEvent) {
$EntryType = switch ($Level) {
"Info" { "Information" }
"Warning" { "Warning" }
"Error" { "Error" }
default { "Information" }
}
try { try {
if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) { if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) {
New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue
} }
$EventMessage = "TaskCategory: $TaskCategory | Message: $Message" Write-EventLog -LogName $EventLog -Source $EventSource `
Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $EntryType -EventId $EventID -Message $EventMessage -EntryType $Level -EventId $EventID -Message $Message
} } catch {
catch { Write-Host "⚠ Failed writing to EventLog: $($_.Exception.Message)" -ForegroundColor Yellow
Write-Host "⚠ [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow
} }
} }
} }
function Write-LogHybrid { function Write-LogHybrid { param($Message,$Level,$TaskCategory,$LogToEvent)
param ( Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory -LogToEvent:$LogToEvent
[string]$Message, }
[ValidateSet("Info", "Warning", "Error", "Success", "General")] } else {
[string]$Level = "Info", function Write-LogHybrid { param($Message,$Level,$TaskCategory,$LogToEvent)
[string]$TaskCategory = "GeneralTask", Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory -LogToEvent:$LogToEvent
[switch]$LogToEvent = $false,
[string]$EventSource = "SVSMSP_Module",
[string]$EventLog = "Application",
[int]$CustomEventID
)
Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID
} }
} }
else { Write-LogHybrid -Message "Starting SVS TaskGate" -Level Info -TaskCategory Startup -LogToEvent
function Write-LogHybrid {
param ( #region Helpers for DattoRMM, LastPass, SVS Module…
[string]$Message, # (Insert your existing Install-DattoRMM-Helper, LastPass and Install-SVSMSP functions here.)
[ValidateSet("Info", "Warning", "Error", "Success", "General")]
[string]$Level = "Info",
[string]$TaskCategory = "GeneralTask",
[switch]$LogToEvent = $false,
[string]$EventSource = "SVSMSP_Module",
[string]$EventLog = "Application",
[int]$CustomEventID
)
Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID
}
}
Write-LogHybrid -Message "Starting SVS TaskGate" -Level "Info" -TaskCategory "SVSTaskGate" -LogToEvent:$true
#endregion #endregion
#region Install-DattoRMM-Helper
function Install-DattoRMM-Helper {
param (
[string]$ApiUrl,
[string]$ApiKey,
[string]$ApiSecretKey,
[switch]$FetchSitesOnly,
[string]$SiteName,
[string]$SiteUID
)
if (-not $ApiUrl -or -not $ApiKey -or -not $ApiSecretKey) {
Write-LogHybrid -Message "Missing required parameters. Please provide ApiUrl, ApiKey, and ApiSecretKey." -Level "Error" -LogToEvent
return
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Write-LogHybrid -Message "Fetching OAuth token..." -Level "Info"
try {
$securePassword = ConvertTo-SecureString -String 'public' -AsPlainText -Force
$apiGenToken = Invoke-WebRequest -Credential (New-Object System.Management.Automation.PSCredential -ArgumentList ('public-client', $securePassword)) `
-Uri ("{0}/auth/oauth/token" -f $ApiUrl) `
-Method 'POST' `
-ContentType 'application/x-www-form-urlencoded' `
-Body ("grant_type=password&username={0}&password={1}" -f $ApiKey, $ApiSecretKey) `
| ConvertFrom-Json
$requestToken = $apiGenToken.access_token
Write-LogHybrid -Message "OAuth token fetched successfully." -Level "Success" -LogToEvent
} catch {
Write-LogHybrid -Message "Failed to fetch OAuth token. Details: $($_.Exception.Message)" -Level "Error" -LogToEvent
return
}
if ($FetchSitesOnly) {
Write-Host "Fetching list of sites from the Datto RMM API..." -ForegroundColor Cyan
try {
$getSites = Invoke-WebRequest -Uri "$ApiUrl/api/v2/account/sites" -Method Get -Headers @{ "Authorization" = "Bearer $requestToken" } -ContentType "application/json"
$sitesJson = $getSites.Content | ConvertFrom-Json
$siteList = $sitesJson.sites | ForEach-Object {
[PSCustomObject]@{ Name = $_.name; UID = $_.uid }
}
Write-Host "Successfully fetched list of sites." -ForegroundColor Green
return $siteList
}
catch {
Write-Host "Failed to fetch sites: $($_.Exception.Message)" -ForegroundColor Red
return
}
}
}
#endregion
#region LastPass Extensions
function ForceInstall-LastPassChrome {
$ExtensionID = "hdokiejnpimakedhajhdlcegeplioahd"
$UpdateURL = "https://clients2.google.com/service/update2/crx"
$RegPath = "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist"
try {
New-Item -Path (Split-Path $RegPath) -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue | Out-Null
Set-ItemProperty -Path $RegPath -Name "1" -Value "$ExtensionID;$UpdateURL" -ErrorAction Stop
Write-Host "Configured LastPass Chrome extension."
}
catch {
Write-Host "Failed Chrome config: $($_.Exception.Message)" -ForegroundColor Red
}
}
function ForceInstall-LastPassEdge {
$ExtensionID = "bbcinlkgjjkejfdpemiealijmmooekmp"
$UpdateURL = "https://edge.microsoft.com/extensionwebstorebase/v1/crx"
$RegPath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallForcelist"
try {
New-Item -Path (Split-Path $RegPath) -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path $RegPath -Force -ErrorAction SilentlyContinue | Out-Null
Set-ItemProperty -Path $RegPath -Name "1" -Value "$ExtensionID;$UpdateURL" -ErrorAction Stop
Write-Host "Configured LastPass Edge extension."
}
catch {
Write-Host "Failed Edge config: $($_.Exception.Message)" -ForegroundColor Red
}
}
function Install-LastPassExtensions { param([switch]$Chrome,[switch]$Edge)
if ($Chrome) { ForceInstall-LastPassChrome }
if ($Edge) { ForceInstall-LastPassEdge }
}
#endregion
#region SVS Module
function Install-SVSMSP {
param (
[switch]$Cleanup,
[switch]$InstallToolkit,
[array]$AllModules = @(@{ModuleName="SVS_Toolkit"},@{ModuleName="SVSMSP"}),
[string]$NewModuleName = "SVSMSP",
[array]$AllRepositories = @(@{RepoName="SVS_Repo"},@{RepoName="SVS_Toolkit"}),
[string]$NewRepositoryName = "SVS_Repo",
[string]$NewRepositoryURL = "http://proget.svstools.ca:8083/nuget/SVS_Repo/",
[string]$LogFilePath = "$env:SVSMSP\svstoolkit.log"
)
function Perform-Cleanup {
Write-LogHybrid -Message "Cleanup mode enabled..." -Level Info -LogToEvent
foreach ($m in $AllModules) {
if (Get-Module -Name $m.ModuleName -ListAvailable) {
Write-LogHybrid -Message "Removing $($m.ModuleName)..." -Level Warning -LogToEvent
try { Uninstall-Module -Name $m.ModuleName -AllVersions -Force; Write-LogHybrid -Message "Removed $($m.ModuleName)" -Level Success -LogToEvent }
catch { Write-LogHybrid -Message "Failed to remove $($m.ModuleName): $_" -Level Error -LogToEvent }
}
}
foreach ($r in $AllRepositories) {
if (Get-PSRepository -Name $r.RepoName -ErrorAction SilentlyContinue) {
Write-LogHybrid -Message "Unregistering $($r.RepoName)..." -Level Warning -LogToEvent
try { Unregister-PSRepository -Name $r.RepoName -ErrorAction Stop; Write-LogHybrid -Message "Unregistered $($r.RepoName)" -Level Success -LogToEvent }
catch { Write-LogHybrid -Message "Failed to unregister $($r.RepoName): $_" -Level Error -LogToEvent }
}
}
Write-LogHybrid -Message "Cleanup complete." -Level Success -LogToEvent
}
function Perform-ToolkitInstallation {
Perform-Cleanup
if ((Get-ExecutionPolicy -Scope LocalMachine) -ne "RemoteSigned") {
Write-LogHybrid -Message "Setting execution policy to RemoteSigned..." -Level Warning -LogToEvent
try { Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force; Write-LogHybrid -Message "Policy set." -Level Success -LogToEvent }
catch { Write-LogHybrid -Message "Failed to set policy: $_" -Level Error -LogToEvent; return }
}
Install-PackageProvider -Name NuGet -Force -Scope AllUsers -Confirm:$false
if (-not (Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) {
Write-LogHybrid -Message "Registering $NewRepositoryName..." -Level Info -LogToEvent
try { Register-PSRepository -Name $NewRepositoryName -SourceLocation $NewRepositoryURL -InstallationPolicy Trusted; Write-LogHybrid -Message "Registered." -Level Success -LogToEvent }
catch { Write-LogHybrid -Message "Register failed: $_" -Level Error -LogToEvent }
}
Write-LogHybrid -Message "Installing $NewModuleName..." -Level Info -LogToEvent
try { Install-Module -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Force; Write-LogHybrid -Message "Installed." -Level Success -LogToEvent }
catch { Write-LogHybrid -Message "Install failed: $_" -Level Error -LogToEvent }
}
Write-LogHybrid -Message "Install-SVSMSP started." -Level Info -LogToEvent
if ($Cleanup) { Perform-Cleanup; return }
if ($InstallToolkit) { Perform-ToolkitInstallation; return }
Perform-ToolkitInstallation
}
#endregion
#region HTTP Listener Setup #region HTTP Listener Setup
try { $listener = New-Object System.Net.HttpListener
$listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://localhost:8081/")
$listener.Prefixes.Add("http://localhost:8081/") $listener.Start()
Write-LogHybrid -Message "Listener prefix added." -Level Info Write-LogHybrid -Message "Listener started on http://localhost:8081/" -Level Info -TaskCategory Listener
$listener.Start()
Write-LogHybrid -Message "Listener started." -Level Info
} catch {
Write-LogHybrid -Message "Listener init error: $($_.Exception.Message)" -Level Error
throw
}
#endregion #endregion
function Get-N8nWebhookData {
param ([string]$AuthHeaderValue)
$url = "https://automate.svstools.ca/webhook/svsmspkit"
$headers = @{ "SVSMSPKit" = $AuthHeaderValue }
try {
$resp = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
foreach ($prop in $resp.PSObject.Properties) {
Set-Variable -Name $prop.Name -Value $prop.Value -Scope Global
}
return $resp
} catch {
Write-Host "Webhook GET error: $($_.Exception.Message)" -ForegroundColor Red
return $null
}
}
#region HTML Content #region HTML UI & CSS
function GetHtmlContent { function GetHtmlContent {
@" @"
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVS TaskGate</title> <title>SVS TaskGate</title>
<link rel="icon" href="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Favicon.ico"> <link rel="icon" href="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Favicon.ico">
<style> <style>
:root { :root {
--background-color: rgba(18, 18, 18, 1); --background-color: rgba(18,18,18,1);
--border-color: rgba(255,127, 0, 0.25); --border-color: rgba(255,127,0,0.25);
--white-color: rgba(255,255,255,1); --white-color: rgba(255,255,255,1);
--gray-color: rgba(102,102,102,1); --gray-color: rgba(102,102,102,1);
--dark-gray-color: rgba( 51, 51, 51,1); --btn-success: rgba(40,167,69,1);
--light-gray-color: rgba(187,187,187,1);
--btn-sidebar-light-gray: rgba( 68, 68, 68,1);
--btn-sidebar-blue: rgba( 30,144,255,1);
--btn-hover: rgba( 0, 86,179,1);
--btn-hover-scale: 1.05;
--btn-success: rgba( 40,167, 69,1);
--btn-success-disabled: rgba(108,117,125,1); --btn-success-disabled: rgba(108,117,125,1);
--btn-danger: rgba(220, 53, 69,1); --btn-danger: rgba(220,53,69,1);
--btn-sidebar-light-gray: rgba(68,68,68,1);
--btn-sidebar-blue: rgba(30,144,255,1);
--btn-hover: rgba(0,86,179,1);
--btn-hover-scale: 1.05;
} }
body { body {
margin:0;padding:0; margin:0; padding:0;
font-family:Arial,sans-serif;
background:var(--background-color); background:var(--background-color);
color:var(--white-color); color:var(--white-color);
height:100%;overflow:hidden; font-family:Arial,sans-serif;
height:100vh; overflow:hidden;
} }
.logo-container { padding:20px; background:var(--background-color); } .logo-container {
.logo-container img { max-width:300px; } padding:20px; background:var(--background-color);
.container { display:flex; height:100vh; overflow:hidden; } }
.logo-container img { max-width:200px; }
.container { display:flex; height:calc(100% - 60px); }
.sidebar { .sidebar {
width:200px; padding:10px; width:200px; padding:10px;
background:var(--background-color); background:var(--background-color);
@@ -312,256 +113,226 @@ function GetHtmlContent {
.sidebar button { .sidebar button {
width:100%; margin-bottom:10px; padding:10px; width:100%; margin-bottom:10px; padding:10px;
background:var(--btn-sidebar-light-gray); background:var(--btn-sidebar-light-gray);
color:var(--white-color); border:none; border-radius:5px; border:none; border-radius:4px;
color:var(--white-color);
text-align:left; cursor:pointer; text-align:left; cursor:pointer;
transition:background 0.3s,transform 0.2s; transition:background 0.2s,transform 0.2s;
}
.sidebar button.active {
background:var(--btn-sidebar-blue);
}
.sidebar button:hover {
background:var(--btn-hover);
transform:scale(var(--btn-hover-scale));
} }
.sidebar button.active { background:var(--btn-sidebar-blue); }
.sidebar button:hover { background:var(--btn-hover); transform:scale(var(--btn-hover-scale)); }
.content { .content {
flex:1; padding:20px; flex:1; padding:20px; overflow-y:auto;
overflow-y:auto; max-height:calc(100vh-50px);
} }
.tab-content { display:none; } .tab-content { display:none; }
.tab-content.active { display:block; } .tab-content.active { display:block; }
.checkbox-group label { display:flex; align-items:center; margin:0; padding:0; line-height:1.5; } .button-group {
.password-input, .dropdown { margin-top:20px; text-align:right;
width:30%; padding:10px; margin-bottom:20px;
background:var(--background-color);
color:var(--white-color);
border:1px solid var(--border-color);
border-radius:5px;
} }
#n8nPasswordContainer { display:none; margin-top:20px; }
.button-group { margin-top:20px; text-align:right; }
.log { display:none !important; }
.button-group button { .button-group button {
display:inline-block; padding:0.75rem 1.5rem; margin-left:10px; padding:0.75rem 1.5rem;
margin-left:0.5rem; font-size:1rem; line-height:1.4; border:none; border-radius:4px; cursor:pointer;
border:none; border-radius:0.375rem; cursor:pointer; font-size:1rem;
transition:opacity 0.1s ease; transition:opacity 0.1s;
} }
.button-group>button:first-child { margin-left:0; }
.install-button { background:var(--btn-success); color:#fff; }
.install-button:disabled { background:var(--btn-success-disabled); cursor:not-allowed; }
.exit-button { background:var(--btn-danger); color:#fff; }
.button-group button:hover:not(:disabled) { opacity:0.9; } .button-group button:hover:not(:disabled) { opacity:0.9; }
@media(max-width:768px){ .container{flex-direction:column;} .sidebar{width:100%;} } .install-button {
background:var(--btn-success); color:#fff;
}
.install-button:disabled {
background:var(--btn-success-disabled); cursor:not-allowed;
}
.exit-button {
background:var(--btn-danger); color:#fff;
}
/* HIDE the old log pane */
.log { display:none !important; }
</style> </style>
</head> </head>
<body> <body>
<div class="logo-container"> <div class="logo-container">
<img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_logo.svg" alt="SVS Logo"> <img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_logo.svg" alt="SVS Logo">
</div> </div>
<div class="container"> <div class="container">
<div class="sidebar"> <div class="sidebar">
<button class="tab-button active" data-tab="onboardTab" aria-expanded="true">On-Boarding</button> <button class="tab-button active" data-tab="onboardTab" aria-expanded="true">On-Boarding</button>
<button class="tab-button" data-tab="offboardTab">Off-Boarding</button> <button class="tab-button" data-tab="tweaksTab">Apply Tweaks</button>
<button class="tab-button" data-tab="tweaksTab">Tweaks</button> <button class="tab-button" data-tab="SVSAppsTab">Install SVS Apps</button>
<button class="tab-button" data-tab="SVSAppsTab">SVS APPs</button>
</div> </div>
<div class="content"> <div class="content">
<!-- On-Boarding Tab --> <!-- On-Boarding (just placeholder for context) -->
<div id="onboardTab" class="tab-content active"> <div id="onboardTab" class="tab-content active">
<h2>On-Boarding</h2> <h2>On-Boarding</h2>
<h3 style="color:var(--gray-color);">This new deployment method ensures everything is deployed smoothly!</h3> <p>your original onboarding controls</p>
<div class="columns-container">
<!-- left and right columns as before -->
<div class="checkbox-group column" id="leftColumn">
<label><input type="checkbox" id="selectAllLeftCheckbox" onclick="toggleLeftColumnCheckboxes(this)"> Select All</label>
<label><input type="checkbox" class="left-checkbox" name="setSVSPowerplan"> Set SVS Powerplan</label>
<label><input type="checkbox" class="left-checkbox" name="installSVSMSPModule"> Install SVSMSP Module</label>
<label><input type="checkbox" class="left-checkbox" name="installCyberQP"> Install CyberQP</label>
<label><input type="checkbox" class="left-checkbox" name="installSplashtop"> Install Splashtop</label>
<label><input type="checkbox" class="left-checkbox" name="installSVSHelpDesk"> Install SVSHelpDesk</label>
<label><input type="checkbox" class="left-checkbox" name="installThreatLocker"> Install ThreatLocker</label>
<label><input type="checkbox" class="left-checkbox" name="installRocketCyber"> Install RocketCyber</label>
<label><input type="checkbox" class="left-checkbox" name="installDattoRMM" onclick="toggleDattoRMMOptions()"> Install DattoRMM</label>
<div id="dattoRMMOptionsContainer" style="display:none; padding-left:20px;">
<label><input type="checkbox" class="left-checkbox" name="dattoRMMOption" value="inputVar"> Copy Site Variables</label>
<label><input type="checkbox" class="left-checkbox" name="dattoRMMOption" value="rmm"> Install DRMM Agent</label>
<label><input type="checkbox" class="left-checkbox" name="dattoRMMOption" value="exe"> Download.exe</label>
</div>
</div>
<div class="checkbox-group column" id="rightColumn">
<label><input type="checkbox" class="right-checkbox" name="EnableBitLocker"> Enable BitLocker</label>
<label><input type="checkbox" class="right-checkbox" name="setedgedefaultsearch"> Set Edge Default Search Engine</label>
</div>
</div>
<div id="n8nPasswordContainer">
<label for="n8nPassword">Enter Password:</label><br>
<input type="password" id="n8nPassword" class="password-input" placeholder="Enter Password">
</div>
<div id="DattoRMMContainer" style="display:none;">
<label for="dattoRmmDropdown">Select a Datto RMM site:</label><br>
<select id="dattoRmmDropdown" class="dropdown">
<option value="">Fetching sites...</option>
$siteOptions
</select>
</div>
<div class="button-group">
<button class="install-button" id="fetchSitesButton" onclick="fetchSites()" disabled>Fetch Sites</button>
<button class="install-button" onclick="triggerInstall()">Install</button>
</div>
</div>
<!-- Off-Boarding Tab -->
<div id="offboardTab" class="tab-content">
<h2>Off-Boarding</h2>
<div class="checkbox-group column">
<label><input type="checkbox" id="selectAllOffboardCheckbox" onclick="toggleOffboardCheckboxes(this)"> Select All</label>
<label><input type="checkbox" name="uninstallSVSMSPModule"> Uninstall SVSMSP Module</label>
<label><input type="checkbox" name="uninstallThreatLocker"> Uninstall ThreatLocker</label>
<label><input type="checkbox" name="uninstallCyberQP"> Uninstall CyberQP</label>
<label><input type="checkbox" name="uninstallDattoEDR"> Uninstall DattoEDR</label>
<label><input type="checkbox" name="uninstallDattoRMM"> Uninstall DattoRMM</label>
<label><input type="checkbox" name="uninstallDattoDEB"> Uninstall DattoDEB</label>
<label><input type="checkbox" name="uninstallRocketCyber"> Uninstall RocketCyber</label>
<label><input type="checkbox" name="uninstallSplashtop"> Uninstall Splashtop</label>
<label><input type="checkbox" name="uninstallSVSHelpdesk"> Uninstall SVSHelpdesk</label>
<label><input type="checkbox" name="uninstallSVSWatchtower"> Uninstall SVSWatchtower</label>
</div>
<div class="button-group">
<button class="install-button" onclick="triggerOffboard()">Offboard</button>
</div>
</div> </div>
<!-- Tweaks Tab --> <!-- Tweaks Tab -->
<div id="tweaksTab" class="tab-content"> <div id="tweaksTab" class="tab-content">
<h2>Tweaks</h2> <h2>Apply Tweaks</h2>
<div class="columns-container"> <div id="tweaksGroup" class="checkbox-group">
<div class="column"> <label><input type="checkbox" id="disableAnimations"> Disable Animations</label><br>
<h3>System Optimizations</h3> <label><input type="checkbox" id="optimizePerformance"> Optimize Performance</label><br>
<label><input type="checkbox" id="setedgedefaultsearchCheckbox" name="setedgedefaultsearch"> Set Edge Default Search Engine</label> <label><input type="checkbox" id="increaseFontSize"> Increase Font Size</label>
<label><input type="checkbox" id="setWindowsPerformanceCheckbox" name="setWindowsPerformance" onclick="toggleRadioButtons(this)"> Optimize Windows Performance</label>
<div id="windowsPerformanceOptions" style="display:none; margin-left:20px;">
<label><input type="radio" name="optimizeWindowsPerformanceLevel" value="full"> Full</label>
<label><input type="radio" name="optimizeWindowsPerformanceLevel" value="partial"> Partial</label>
<label><input type="radio" name="optimizeWindowsPerformanceLevel" value="none" checked> None</label>
</div>
<label><input type="checkbox" id="stopUnnecessaryServicesCheckbox" name="stopUnnecessaryServices"> Stop Unnecessary Services</label>
</div>
<div class="column">
<h3>Additional Tweaks</h3>
<label><input type="checkbox" id="disableAnimationsCheckbox" name="disableAnimations"> Disable Animations</label>
<label><input type="checkbox" id="optimizePerformanceCheckbox" name="optimizePerformance"> Optimize Application Performance</label>
<label><input type="checkbox" id="increaseFontSizeCheckbox" name="increaseFontSize"> Increase Font Size</label>
</div>
<div class="column">
<h3>Miscellaneous</h3>
<label><input type="checkbox" id="enableDarkModeCheckbox" name="enableDarkMode"> Enable Dark Mode</label>
<label><input type="checkbox" id="clearTempFilesCheckbox" name="clearTempFiles"> Clear Temporary Files</label>
</div>
</div> </div>
<div class="button-group"> <div class="button-group">
<button class="install-button" onclick="triggerTweaks()">Apply Tweaks</button> <button class="install-button" onclick="triggerTweaks()">Apply Tweaks</button>
</div> </div>
</div> </div>
<!-- SVS APPs Tab -->
<!-- SVS Apps Tab -->
<div id="SVSAppsTab" class="tab-content"> <div id="SVSAppsTab" class="tab-content">
<h2>SVS APPs</h2> <h2>Install SVS Apps</h2>
<div class="checkbox-group column" id="wingetGroup"> <div id="wingetGroup" class="checkbox-group">
<h3>Winget Apps</h3> <h4>Winget Apps</h4>
<label><input type="checkbox" id="selectAllWingetCheckbox" onclick="toggleWingetCheckboxes(this)"> Select All</label> <label><input class="winget-checkbox" name="wingetLastpass" type="checkbox"> LastPass</label><br>
<label><input type="checkbox" class="winget-checkbox" name="wingetLastpass"> LastPass Desktop App</label> <label><input class="winget-checkbox" name="wingetBitwarden" type="checkbox"> Bitwarden</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetBitwarden"> Bitwarden Desktop App</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetICloud"> Apple iCloud</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetBitvise"> Bitvise SSH Client</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetObsidian"> Obsidian</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetFlameshot"> Flameshot</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetVSCODE"> Visual Studio Code</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetGit"> Git.Git</label>
<label><input type="checkbox" class="winget-checkbox" name="wingetSublimeText"> SublimeText 4</label>
</div> </div>
<div class="checkbox-group column" id="extensionsGroup"> <div id="extensionsGroup" class="checkbox-group">
<h3>Extensions</h3> <h4>Browser Extensions</h4>
<label><input type="checkbox" class="extension-checkbox" name="installLastPassChrome"> LastPass Extension for Chrome</label> <label><input class="extension-checkbox" name="installLastPassChrome" type="checkbox"> LastPass Chrome</label><br>
<label><input type="checkbox" class="extension-checkbox" name="installLastPassEdge"> LastPass Extension for Edge</label> <label><input class="extension-checkbox" name="installLastPassEdge" type="checkbox"> LastPass Edge</label>
</div> </div>
<div class="button-group"> <div class="button-group">
<button class="install-button" onclick="triggerSVSApps()">Install SVS Apps</button> <button class="install-button" onclick="triggerSVSApps()">Install SVS Apps</button>
</div> </div>
</div> </div>
<!-- Exit -->
<div class="button-group">
<button class="exit-button" onclick="endSession()">Exit</button>
</div>
<!-- Log area is present but hidden via CSS -->
<div class="log" id="logArea"><p>Logs will appear here...</p></div>
</div> </div>
</div> </div>
<script> <script>
// All your existing JS: tab navigation, toggle functions, triggerInstall, fetchLogs, etc. // 1) TAB NAVIGATION
const tabButtons = document.querySelectorAll('.tab-button');
const tabContents = document.querySelectorAll('.tab-content');
tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
tabButtons.forEach(b=>{ b.classList.remove('active'); b.setAttribute('aria-expanded','false') });
tabContents.forEach(c=>c.classList.remove('active'));
btn.classList.add('active');
btn.setAttribute('aria-expanded','true');
document.getElementById(btn.dataset.tab).classList.add('active');
});
});
// 2) TRIGGER APPLY TWEAKS
function triggerTweaks() {
const tweaks = Array.from(
document.querySelectorAll('#tweaksGroup input[type="checkbox"]')
)
.filter(cb=>cb.checked)
.map(cb=>cb.id);
if (tweaks.length === 0) {
alert("Select at least one tweak.");
return;
}
fetch('/runTweaks', {
method: 'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({ tweaks })
})
.then(r=>r.ok ? alert("Tweaks applied!") : alert("Error applying tweaks"))
.catch(e=>alert("Network error: "+e));
}
// 3) TRIGGER INSTALL SVS APPS
function triggerSVSApps() {
const wingetSel = Array.from(
document.querySelectorAll('.winget-checkbox')
).filter(cb=>cb.checked).map(cb=>cb.name);
const extSel = Array.from(
document.querySelectorAll('.extension-checkbox')
).filter(cb=>cb.checked).map(cb=>cb.name);
if (wingetSel.length+extSel.length === 0) {
alert("Select at least one app or extension.");
return;
}
fetch('/installSVSApps', {
method: 'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({ winget: wingetSel, extensions: extSel })
})
.then(r=>r.ok ? alert("Installation triggered!") : alert("Error triggering install"))
.catch(e=>alert("Network error: "+e));
}
</script> </script>
</body> </body>
</html> </html>
"@ "@
} }
# Launch the UI #endregion
# 3) LAUNCH THE UI
Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/" Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/"
#region HTTP Listener Route Handling
#region 4) ROUTE HANDLING
try { try {
while ($listener.IsListening) { while ($listener.IsListening) {
$ctx = $listener.GetContext() $ctx = $listener.GetContext()
$req = $ctx.Request $req = $ctx.Request
$res = $ctx.Response $res = $ctx.Response
switch ($req.Url.AbsolutePath) {
"/" { switch ($req.Url.AbsolutePath) {
$html = GetHtmlContent "/" {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($html) $html = GetHtmlContent
$res.ContentType = "text/html" $bytes = [Text.Encoding]::UTF8.GetBytes($html)
$res.ContentLength64 = $bytes.Length $res.ContentType = "text/html"
$res.OutputStream.Write($bytes,0,$bytes.Length) $res.ContentLength64 = $bytes.Length
$res.OutputStream.Close() $res.OutputStream.Write($bytes,0,$bytes.Length)
} $res.OutputStream.Close()
"/getn8npw" { }
if ($req.HttpMethod -eq "POST") {
$body = (New-Object IO.StreamReader $req.InputStream).ReadToEnd() | ConvertFrom-Json "/runTweaks" {
Get-N8nWebhookData -AuthHeaderValue $body.password if ($req.HttpMethod -eq "POST") {
$sites = Install-DattoRMM-Helper -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey -FetchSitesOnly $body = (New-Object IO.StreamReader $req.InputStream).ReadToEnd() | ConvertFrom-Json
if ($sites) { Write-LogHybrid -Message "Tweaks: $($body.tweaks -join ', ')" -Level Info -TaskCategory Tweaks
$json = $sites | ConvertTo-Json # TODO: call your actual tweak functions here...
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json) $res.StatusCode = 200
$res.ContentType = "application/json" $res.OutputStream.Write(([Text.Encoding]::UTF8.GetBytes("OK")),0,2)
$res.ContentLength64 = $bytes.Length
$res.OutputStream.Write($bytes,0,$bytes.Length)
} else {
$res.StatusCode = 500
$res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("No sites found")),0,14)
}
$res.OutputStream.Close()
}
}
"/installSVSMSPModule" {
if ($req.HttpMethod -eq "GET") {
try { Install-SVSMSP -InstallToolkit; $msg="SVS Module triggered"; $code=200 }
catch { $msg="Error: $_"; $code=500 }
$res.StatusCode = $code
$bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
$res.OutputStream.Write($bytes,0,$bytes.Length)
$res.OutputStream.Close()
}
}
# … include the rest of your endpoints (/installrmm, /setSVSPowerplan, /installCyberQP, etc.) exactly as before …
"/quit" {
if ($req.HttpMethod -eq "GET") {
$res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("Shutting down")),0,12)
$res.OutputStream.Close()
$listener.Stop()
break
}
}
default {
$res.StatusCode = 404
$res.OutputStream.Write(([System.Text.Encoding]::UTF8.GetBytes("Not Found")),0,9)
$res.OutputStream.Close()
}
} }
}
"/installSVSApps" {
if ($req.HttpMethod -eq "POST") {
$data = (New-Object IO.StreamReader $req.InputStream).ReadToEnd() | ConvertFrom-Json
Write-LogHybrid -Message "Winget: $($data.winget -join ', ')" -Level Info -TaskCategory "SVSApps"
Write-LogHybrid -Message "Extensions: $($data.extensions -join ', ')" -Level Info -TaskCategory "SVSApps"
# TODO: invoke your winget & extension helpers here...
$res.StatusCode = 200
$res.OutputStream.Write(([Text.Encoding]::UTF8.GetBytes("OK")),0,2)
}
}
"/quit" {
$res.StatusCode = 200
$res.OutputStream.Write(([Text.Encoding]::UTF8.GetBytes("bye")),0,3)
$listener.Stop()
break
}
default {
$res.StatusCode = 404
$res.OutputStream.Write(([Text.Encoding]::UTF8.GetBytes("Not Found")),0,9)
}
} }
}
} }
finally { finally {
if ($listener) { if ($listener) {
Write-LogHybrid -Message "Stopping listener." -Level Info Write-LogHybrid -Message "Stopping listener" -Level Info -TaskCategory Listener
$listener.Stop() $listener.Stop(); $listener.Close()
$listener.Close() }
}
} }