657 lines
25 KiB
PowerShell
657 lines
25 KiB
PowerShell
### To Modify as of January 27 2025
|
|
|
|
### let's start thinking about the write-log -TaskCategory "On-boarding" or "Off-boarding"
|
|
### need RGB color codes form 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])) {
|
|
$Global:LogCache = New-Object System.Collections.ArrayList
|
|
}
|
|
|
|
#region Write-LogHelper
|
|
if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) {
|
|
function Write-LogHelper {
|
|
param (
|
|
[string]$Message,
|
|
[ValidateSet("Info", "Warning", "Error", "Success", "General")]
|
|
[string]$Level = "Info",
|
|
[string]$TaskCategory = "GeneralTask",
|
|
[switch]$LogToEvent = $false,
|
|
[string]$EventSource = "SVSMSP_Module",
|
|
[string]$EventLog = "Application",
|
|
[int]$CustomEventID
|
|
)
|
|
$EventID = switch ($Level) {
|
|
"Info" { 1000 }
|
|
"Warning" { 2000 }
|
|
"Error" { 3000 }
|
|
"Success" { 4000 }
|
|
"General" { 1000 }
|
|
}
|
|
$Icon = switch ($Level) {
|
|
"Info" { [System.Char]::ConvertFromUtf32(0x1F4CB) }
|
|
"Warning" { ([char]0x26A0) }
|
|
"Error" { ([char]0x274C) }
|
|
"Success" { ([char]0x2705) }
|
|
"General" { ([char]0x1F4E6) }
|
|
}
|
|
$Color = switch ($Level) {
|
|
"Info" { "Cyan" }
|
|
"Warning" { "Yellow" }
|
|
"Error" { "Red" }
|
|
"Success" { "Green" }
|
|
"General" { "White" }
|
|
}
|
|
$logEntry = [PSCustomObject]@{
|
|
Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
Level = $Level
|
|
Message = "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)"
|
|
}
|
|
[void]$Global:LogCache.Add($logEntry)
|
|
if ($LogToEvent) {
|
|
$EntryType = switch ($Level) {
|
|
"Info" { "Information" }
|
|
"Warning" { "Warning" }
|
|
"Error" { "Error" }
|
|
default { "Information" }
|
|
}
|
|
try {
|
|
if (-not (Get-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 -EntryType $EntryType -EventId $EventID -Message $EventMessage
|
|
}
|
|
catch {
|
|
Write-Host "([char]0x26A0) [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow
|
|
}
|
|
}
|
|
}
|
|
function Write-LogHybrid {
|
|
param (
|
|
[string]$Message,
|
|
[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-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory `
|
|
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
|
|
-CustomEventID $CustomEventID
|
|
}
|
|
}
|
|
else {
|
|
function Write-LogHybrid {
|
|
param (
|
|
[string]$Message,
|
|
[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
|
|
|
|
#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 -TypeName 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
|
|
}
|
|
$getHeaders = @{"Authorization" = "Bearer $requestToken"}
|
|
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 $getHeaders -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 from the API. Details: $($_.Exception.Message)" -ForegroundColor Red
|
|
return
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region LastPass Extensions
|
|
function ForceInstall-LastPassChrome {
|
|
# Chrome (Web Store) extension ID and update URL for LastPass
|
|
$ExtensionID_LastPass_Chrome = "hdokiejnpimakedhajhdlcegeplioahd"
|
|
$ChromeUpdateURL = "https://clients2.google.com/service/update2/crx"
|
|
$ChromePolicyRegPath = "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist"
|
|
try {
|
|
New-Item -Path "HKLM:\SOFTWARE\Policies\Google" -Force -ErrorAction SilentlyContinue | Out-Null
|
|
New-Item -Path "HKLM:\SOFTWARE\Policies\Google\Chrome" -Force -ErrorAction SilentlyContinue | Out-Null
|
|
New-Item -Path $ChromePolicyRegPath -Force -ErrorAction SilentlyContinue | Out-Null
|
|
$chromeValue = "$ExtensionID_LastPass_Chrome;$ChromeUpdateURL"
|
|
Set-ItemProperty -Path $ChromePolicyRegPath -Name "1" -Value $chromeValue -ErrorAction Stop
|
|
Write-Host "Successfully configured LastPass in Chrome ExtensionInstallForcelist."
|
|
}
|
|
catch {
|
|
Write-Host "Failed to configure Chrome: $($_.Exception.Message)" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
function ForceInstall-LastPassEdge {
|
|
# Edge (Add-ons Store) extension ID and update URL for LastPass
|
|
$ExtensionID_LastPass_Edge = "bbcinlkgjjkejfdpemiealijmmooekmp"
|
|
$EdgeUpdateURL = "https://edge.microsoft.com/extensionwebstorebase/v1/crx"
|
|
$EdgePolicyRegPath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallForcelist"
|
|
try {
|
|
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft" -Force -ErrorAction SilentlyContinue | Out-Null
|
|
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Force -ErrorAction SilentlyContinue | Out-Null
|
|
New-Item -Path $EdgePolicyRegPath -Force -ErrorAction SilentlyContinue | Out-Null
|
|
$edgeValue = "$ExtensionID_LastPass_Edge;$EdgeUpdateURL"
|
|
Set-ItemProperty -Path $EdgePolicyRegPath -Name "1" -Value $edgeValue -ErrorAction Stop
|
|
Write-Host "Successfully configured LastPass in Edge ExtensionInstallForcelist."
|
|
}
|
|
catch {
|
|
Write-Host "Failed to configure Edge: $($_.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,
|
|
[Parameter(Mandatory = $false)]
|
|
[array]$AllModules = @(
|
|
@{ ModuleName = "SVS_Toolkit" },
|
|
@{ ModuleName = "SVSMSP" }
|
|
),
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$NewModuleName = "SVSMSP",
|
|
[Parameter(Mandatory = $false)]
|
|
[array]$AllRepositories = @(
|
|
@{ RepoName = "SVS_Repo" },
|
|
@{ RepoName = "SVS_Toolkit" }
|
|
),
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$NewRepositoryName = "SVS_Repo",
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$NewRepositoryURL = "http://proget.svstools.ca:8083/nuget/SVS_Repo/",
|
|
[Parameter(Mandatory = $false)]
|
|
[array]$CommandsToCheck = @(
|
|
"Install-DattoRMM",
|
|
"Install-CyberQP",
|
|
"Install-RocketCyber",
|
|
"Install-Splashtop",
|
|
"Install-ThreatLocker",
|
|
"Install-SVSHelpdesk"
|
|
),
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$LogFilePath = "$env:SVSMSP\svstoolkit.log"
|
|
)
|
|
function Perform-Cleanup {
|
|
Write-LogHybrid -Message "Cleanup mode enabled. Starting cleanup process..." -Level "Info" -LogToEvent
|
|
Write-LogHybrid -Message "Starting cleanup of old modules..." -Level "Info" -LogToEvent
|
|
foreach ($module in $AllModules) {
|
|
$ModuleName = $module.ModuleName
|
|
if (Get-Module -Name $ModuleName -ListAvailable) {
|
|
Write-LogHybrid -Message "Removing module '$ModuleName'..." -Level "Warning" -LogToEvent
|
|
try {
|
|
Get-Module -Name $ModuleName -ListAvailable | ForEach-Object {
|
|
Uninstall-Module -Name $_.Name -AllVersions -Force
|
|
}
|
|
Write-LogHybrid -Message "Module '$ModuleName' removed successfully." -Level "Success" -LogToEvent
|
|
}
|
|
catch {
|
|
Write-LogHybrid -Message "Failed to remove module '$ModuleName'. Error: $($_.Exception.Message)" -Level "Error" -LogToEvent
|
|
}
|
|
}
|
|
else {
|
|
Write-LogHybrid -Message "Module '$ModuleName' not found. Skipping..." -Level "Info" -LogToEvent
|
|
}
|
|
}
|
|
Write-LogHybrid -Message "Starting cleanup of old repositories..." -Level "Info" -LogToEvent
|
|
foreach ($repo in $AllRepositories) {
|
|
$RepoName = $repo.RepoName
|
|
Write-LogHybrid -Message "Removing repository '$RepoName'..." -Level "Warning" -LogToEvent
|
|
if (Get-PSRepository -Name $RepoName -ErrorAction SilentlyContinue) {
|
|
try {
|
|
Unregister-PSRepository -Name $RepoName -ErrorAction Stop
|
|
Write-LogHybrid -Message "Repository '$RepoName' removed successfully." -Level "Success" -LogToEvent
|
|
}
|
|
catch {
|
|
Write-LogHybrid -Message "Failed to remove repository '$RepoName'. Error: $($_.Exception.Message)" -Level "Error" -LogToEvent
|
|
}
|
|
}
|
|
else {
|
|
Write-LogHybrid -Message "Repository '$RepoName' does not exist. Skipping removal." -Level "Info" -LogToEvent
|
|
}
|
|
}
|
|
Write-LogHybrid -Message "Cleanup process completed successfully." -Level "Success" -LogToEvent
|
|
}
|
|
function Perform-ToolkitInstallation {
|
|
Perform-Cleanup
|
|
$localMachineExecutionPolicy = Get-ExecutionPolicy -Scope LocalMachine
|
|
if ($localMachineExecutionPolicy -ne "RemoteSigned") {
|
|
Write-LogHybrid -Message "Setting execution policy to RemoteSigned..." -Level "Warning" -LogToEvent
|
|
try {
|
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force
|
|
Write-LogHybrid -Message "Execution policy set to RemoteSigned successfully." -Level "Success" -LogToEvent
|
|
}
|
|
catch {
|
|
Write-LogHybrid -Message "Failed to set execution policy. Error: $_" -Level "Error" -LogToEvent
|
|
return
|
|
}
|
|
}
|
|
Install-PackageProvider -Name "NuGet" -Force -Scope AllUsers -Confirm:$false
|
|
Write-LogHybrid -Message "Registering the new repository '$NewRepositoryName'..." -Level "Info" -LogToEvent
|
|
try {
|
|
if (!(Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) {
|
|
Register-PSRepository -Name $NewRepositoryName -SourceLocation $NewRepositoryURL -InstallationPolicy Trusted
|
|
Write-LogHybrid -Message "Repository '$NewRepositoryName' registered successfully." -Level "Success" -LogToEvent
|
|
}
|
|
}
|
|
catch {
|
|
Write-LogHybrid -Message "Failed to register new repository '$NewRepositoryName'. Error: $($_.Exception.Message)" -Level "Error" -LogToEvent
|
|
}
|
|
Write-LogHybrid -Message "Installing the new module '$NewModuleName'..." -Level "Info" -LogToEvent
|
|
try {
|
|
Install-Module -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Force
|
|
Write-LogHybrid -Message "Module '$NewModuleName' installed successfully." -Level "Success" -LogToEvent
|
|
}
|
|
catch {
|
|
Write-LogHybrid -Message "Failed to install new module '$NewModuleName'. Error: $($_.Exception.Message)" -Level "Error" -LogToEvent
|
|
}
|
|
Write-LogHybrid -Message "Toolkit installation process completed successfully." -Level "Success" -LogToEvent
|
|
}
|
|
Write-LogHybrid -Message "Install-SVSMSP function started." -Level "Info" -LogToEvent
|
|
if ($Cleanup) {
|
|
Perform-Cleanup
|
|
return
|
|
}
|
|
if ($InstallToolkit) {
|
|
Perform-ToolkitInstallation
|
|
return
|
|
}
|
|
Write-LogHybrid -Message "No specific mode specified. Defaulting to toolkit installation mode..." -Level "Info" -LogToEvent
|
|
Perform-ToolkitInstallation
|
|
}
|
|
#endregion SVS Module
|
|
|
|
#region HTTP Listener Setup
|
|
try {
|
|
$listener = New-Object System.Net.HttpListener
|
|
if (-not $listener) {
|
|
throw "Failed to initialize HttpListener."
|
|
}
|
|
$listener.Prefixes.Add("http://localhost:8081/")
|
|
Write-LogHybrid -Message "Listener initialized with prefix http://localhost:8081/" -Level "Info"
|
|
$listener.Start()
|
|
Write-LogHybrid -Message "Listener started successfully." -Level "Info"
|
|
} catch {
|
|
Write-LogHybrid -Message "Critical error initializing listener: $($_.Exception.Message)" -Level "Error"
|
|
throw $_
|
|
}
|
|
#endregion
|
|
|
|
function Get-N8nWebhookData {
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AuthHeaderValue
|
|
)
|
|
$url = "https://automate.svstools.ca/webhook/svsmspkit"
|
|
$headers = @{
|
|
"SVSMSPKit" = $AuthHeaderValue
|
|
}
|
|
try {
|
|
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
|
|
Write-Host "Response received successfully:" -ForegroundColor Green
|
|
$data = $response
|
|
$global:Comment_SVSmodule = $data._Comment_SVSmodule
|
|
$global:ModuleName = $data.ModuleName
|
|
$global:RepositoryURL = $data.RepositoryURL
|
|
$global:OldRepo = $data.OldRepo
|
|
$global:NewRepo = $data.NewRepo
|
|
$global:CommandsToCheck = $data.CommandsToCheck
|
|
$global:LogFilePath = $data.LogFilePath
|
|
$global:Comment_DRMM = $data._Comment_DRMM
|
|
$global:ApiUrl = $data.ApiUrl
|
|
$global:ApiKey = $data.ApiKey
|
|
$global:ApiSecretKey = $data.ApiSecretKey
|
|
}
|
|
catch {
|
|
Write-Host "Error making the GET request:" -ForegroundColor Red
|
|
Write-Host $_.Exception.Message
|
|
return $null
|
|
}
|
|
}
|
|
|
|
#region HTML Content
|
|
function GetHtmlContent {
|
|
@"
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>SVS TaskGate</title>
|
|
<link rel="icon" href="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Favicon.ico" type="image/x-icon">
|
|
<style>
|
|
:root {
|
|
/* Cool Palette */
|
|
--background-color: rgba(18, 18, 18, 1);
|
|
--border-color: rgba(255, 127, 0, 0.25);
|
|
/* Neutral Colors */
|
|
--white-color: rgba(255, 255, 255);
|
|
--gray-color: rgba(102, 102, 102);
|
|
--dark-gray-color: rgba(51, 51, 51);
|
|
--light-gray-color: rgba(187, 187, 187);
|
|
/* Sidebar Button Colors */
|
|
--btn-sidebar-light-gray: rgba(68, 68, 68);
|
|
--btn-sidebar-blue: rgba(30, 144, 255, 1);
|
|
--btn-hover: rgba(0, 86, 179, 1);
|
|
--btn-hover-scale: 1.05;
|
|
/* Fixed “go” button colors */
|
|
--btn-success: rgba(40, 167, 69, 1);
|
|
--btn-success-disabled: rgba(108, 117, 125, 1);
|
|
--btn-danger: rgba(220, 53, 69, 1);
|
|
}
|
|
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: var(--background-color);
|
|
color: var(--white-color);
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.logo-container {
|
|
text-align: left;
|
|
padding: 20px;
|
|
background-color: var(--background-color);
|
|
}
|
|
|
|
.logo-container img {
|
|
max-width: 300px;
|
|
height: auto;
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 1.2rem;
|
|
color: var(--gray-color);
|
|
margin-top: 0.5em;
|
|
}
|
|
|
|
.container {
|
|
display: flex;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar {
|
|
width: 200px;
|
|
height: 100%;
|
|
background-color: var(--background-color);
|
|
padding: 10px;
|
|
}
|
|
|
|
.sidebar button {
|
|
display: block;
|
|
width: 100%;
|
|
margin-bottom: 10px;
|
|
padding: 10px;
|
|
color: var(--white-color);
|
|
background-color: var(--btn-sidebar-light-gray);
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
|
}
|
|
|
|
.sidebar button.active {
|
|
background-color: var(--btn-sidebar-blue);
|
|
}
|
|
|
|
.sidebar button:hover {
|
|
background-color: var(--btn-hover);
|
|
transform: scale(var(--btn-hover-scale));
|
|
}
|
|
|
|
.content {
|
|
flex: 1;
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
max-height: calc(100vh - 50px);
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
.checkbox-group label {
|
|
margin: 0;
|
|
padding: 0;
|
|
line-height: 1.5;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.password-input,
|
|
.dropdown {
|
|
width: 30%;
|
|
padding: 10px;
|
|
margin-bottom: 20px;
|
|
border-radius: 5px;
|
|
border: 1px solid var(--border-color);
|
|
background-color: var(--background-color);
|
|
color: var(--white-color);
|
|
}
|
|
|
|
#n8nPasswordContainer {
|
|
display: none;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.button-group {
|
|
margin-top: 20px;
|
|
text-align: right;
|
|
}
|
|
|
|
/* Hide the entire log pane */
|
|
.log {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Uniform styling for all .button-group buttons */
|
|
.button-group button {
|
|
display: inline-block;
|
|
padding: 0.75rem 1.5rem;
|
|
margin-left: 0.5rem;
|
|
font-size: 1rem;
|
|
line-height: 1.4;
|
|
text-align: center;
|
|
border: none;
|
|
border-radius: 0.375rem;
|
|
cursor: pointer;
|
|
transition: opacity 0.1s ease;
|
|
}
|
|
|
|
/* Remove left margin on first button */
|
|
.button-group > button:first-child {
|
|
margin-left: 0;
|
|
}
|
|
|
|
/* Green “go” buttons */
|
|
.install-button {
|
|
background-color: var(--btn-success);
|
|
color: #fff;
|
|
}
|
|
.install-button:disabled {
|
|
background-color: var(--btn-success-disabled);
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Red “danger” buttons */
|
|
.exit-button {
|
|
background-color: var(--btn-danger);
|
|
color: #fff;
|
|
}
|
|
|
|
/* Hover effect */
|
|
.button-group button:hover:not(:disabled) {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
flex-direction: column;
|
|
}
|
|
.sidebar {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="logo-container">
|
|
<img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_logo.svg" alt="SVS Logo">
|
|
</div>
|
|
<div class="container">
|
|
<div class="sidebar">
|
|
<button class="tab-button active" data-tab="onboardTab" aria-expanded="true">On-Boarding</button>
|
|
<button class="tab-button" data-tab="offboardTab" aria-expanded="false">Off-Boarding</button>
|
|
<button class="tab-button" data-tab="tweaksTab" aria-expanded="false">Tweaks</button>
|
|
<button class="tab-button" data-tab="SVSAppsTab" aria-expanded="false">SVS APPs</button>
|
|
</div>
|
|
<div class="content">
|
|
<!-- On-Boarding Tab -->
|
|
<div id="onboardTab" class="tab-content active">
|
|
<h2>On-Boarding</h2>
|
|
<h3 class="subtitle">This new deployment method ensures everything is successfully deployed with greater ease!</h3>
|
|
<div class="columns-container">
|
|
<!-- leftColumn, rightColumn, etc... (unchanged) -->
|
|
</div>
|
|
<div id="n8nPasswordContainer" style="display: none;">
|
|
<label for="n8nPassword">Enter Password:</label><br>
|
|
<input type="password" id="n8nPassword" class="password-input" placeholder="Enter Password">
|
|
</div>
|
|
<br>
|
|
<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, Tweaks, SVS Apps tabs all unchanged -->
|
|
<!-- ... -->
|
|
<!-- Shared Exit Button -->
|
|
<div class="button-group">
|
|
<button class="exit-button" onclick="endSession()">Exit</button>
|
|
</div>
|
|
<!-- Log Area (now hidden via CSS) -->
|
|
<div class="log" id="logArea">
|
|
<p>Logs will appear here...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// (All your existing JS unchanged: tab nav, toggle functions, triggerInstall, fetchLogs, etc.)
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"@
|
|
}
|
|
# Save and launch the HTML
|
|
Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/"
|
|
|
|
#region HTTP Listener Route Handling
|
|
try {
|
|
while ($listener.IsListening) {
|
|
# (All your listener routing code as before)
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "Error: $($_.Exception.Message)"
|
|
}
|
|
finally {
|
|
if ($listener -ne $null) {
|
|
try {
|
|
Write-LogHybrid -Message "Stopping the listener." -Level "Info"
|
|
$listener.Stop()
|
|
$listener.Close()
|
|
Write-LogHybrid -Message "Listener stopped successfully." -Level "Info"
|
|
} catch {
|
|
Write-LogHybrid -Message "Error stopping the listener: $($_.Exception.Message)" -Level "Error"
|
|
}
|
|
} else {
|
|
Write-LogHybrid -Message "Listener object is null; nothing to stop." -Level "Warning"
|
|
}
|
|
}
|