Update SVSTaskGate.ps1

This commit is contained in:
2025-01-02 23:37:30 -05:00
parent a59201c1ce
commit f4beac86ac

View File

@@ -1,84 +1,92 @@
### now that we have a field for user to enter N8N password we need to figure out how we can make the fetch site button refresh the downdown after
### add tweek to set default provider, add to toolkit? ### add tweek to set default provider, add to toolkit?
### power settings in tweeks or onboarding? ### power settings in tweeks or onboarding?
### make script install the toolkit
### test the write-log in the log box and in the event viewer
### need to see if there's anything else we could take from Theo script? ### need to see if there's anything else we could take from Theo script?
### make when using select all the datto rmm pus var runs before install-splashtop ### make when using select all the datto rmm pus var runs before install-splashtop
# ---------------------------------------------------------------------------
# 1) CREATE A GLOBAL LOG CACHE (NEW)
# ---------------------------------------------------------------------------
$Global:LogCache = New-Object System.Collections.ArrayList
# Check if the Write-Log function exists # Check if the Write-Log function exists
if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) { if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction SilentlyContinue)) {
# If the Write-Log function doesn't exist, create the Write-LogHelper function # If the Write-Log function doesn't exist, create the Write-LogHelper function
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", # Task Category for the log entry [string]$TaskCategory = "GeneralTask", # Task Category for the log entry
[switch]$LogToEvent = $false, # Log to Windows Event Log [switch]$LogToEvent = $false, # Log to Windows Event Log
[string]$EventSource = "SVSMSP_Module", # Event Source [string]$EventSource = "SVSMSP_Module", # Event Source
[string]$EventLog = "Application", # Event Log (default: Application) [string]$EventLog = "Application", # Event Log (default: Application)
[int]$CustomEventID # Optional custom Event ID [int]$CustomEventID # Optional custom Event ID
) )
# Simplified Event ID mapping # Simplified Event ID mapping
$EventID = switch ($Level) { $EventID = switch ($Level) {
"Info" { 1000 } "Info" { 1000 }
"Warning" { 2000 } "Warning" { 2000 }
"Error" { 3000 } "Error" { 3000 }
"Success" { 4000 } "Success" { 4000 }
"General" { 1000 } "General" { 1000 }
}
# Icons for each level
$Icon = switch ($Level) {
"Info" { [System.Char]::ConvertFromUtf32(0x1F4CB) } # Information icon
"Warning" { ([char]0x26A0) } # Warning icon
"Error" { ([char]0x274C) } # Error icon
"Success" { ([char]0x2705) } # Success icon
"General" { ([char]0x1F4E6) } # Package icon for generic tasks/operations
}
# Map levels to colors
$Color = switch ($Level) {
"Info" { "Cyan" }
"Warning" { "Yellow" }
"Error" { "Red" }
"Success" { "Green" }
"General" { "White" }
}
# Include Task Category, Icon, and Message in the console output
Write-Host "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)" -ForegroundColor $Color
# Optionally log to the Windows Event Log
if ($LogToEvent) {
$EntryType = switch ($Level) {
"Info" { "Information" }
"Warning" { "Warning" }
"Error" { "Error" }
default { "Information" }
} }
try { # Icons for each level
# Check if the event source exists, create it if necessary $Icon = switch ($Level) {
if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) { "Info" { [System.Char]::ConvertFromUtf32(0x1F4CB) } # Information icon
New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue "Warning" { ([char]0x26A0) } # Warning icon
"Error" { ([char]0x274C) } # Error icon
"Success" { ([char]0x2705) } # Success icon
"General" { ([char]0x1F4E6) } # Package icon
}
# Map levels to colors
$Color = switch ($Level) {
"Info" { "Cyan" }
"Warning" { "Yellow" }
"Error" { "Red" }
"Success" { "Green" }
"General" { "White" }
}
# Write to the PowerShell console
Write-Host "$Icon [$Level] [$TaskCategory] $Message (Event ID: $EventID)" -ForegroundColor $Color
# -------------------------------------------------------------------
# 2) ALSO STORE THE LOG IN OUR GLOBAL LOG CACHE (NEW)
# -------------------------------------------------------------------
$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)
# -------------------------------------------------------------------
# Optionally log to the Windows Event Log
if ($LogToEvent) {
$EntryType = switch ($Level) {
"Info" { "Information" }
"Warning" { "Warning" }
"Error" { "Error" }
default { "Information" }
} }
# Include Task Category in the Event Log Message try {
$EventMessage = "TaskCategory: $TaskCategory | Message: $Message" if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) {
New-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue
# Write the event log }
Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $EntryType -EventId $EventID -Message $EventMessage $EventMessage = "TaskCategory: $TaskCategory | Message: $Message"
} catch { Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $EntryType -EventId $EventID -Message $EventMessage
Write-Host "([char]0x26A0) [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow }
catch {
Write-Host "([char]0x26A0) [Warning] [EventLog] Failed to write to Event Log: $($_.Exception.Message)" -ForegroundColor Yellow
}
} }
} }
}
# Define a fallback Write-LogHybrid function that uses Write-LogHelper
function Write-LogHybrid { function Write-LogHybrid {
param ( param (
[string]$Message, [string]$Message,
@@ -90,8 +98,6 @@ if (-not (Get-Command -Name Write-Log -CommandType Function -ErrorAction Silentl
[string]$EventLog = "Application", [string]$EventLog = "Application",
[int]$CustomEventID [int]$CustomEventID
) )
# Use the helper function to log
Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory ` Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog ` -LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID -CustomEventID $CustomEventID
@@ -110,8 +116,6 @@ else {
[string]$EventLog = "Application", [string]$EventLog = "Application",
[int]$CustomEventID [int]$CustomEventID
) )
# Use the existing Write-Log function
Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory ` Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory `
-LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog ` -LogToEvent:$LogToEvent -EventSource $EventSource -EventLog $EventLog `
-CustomEventID $CustomEventID -CustomEventID $CustomEventID
@@ -120,6 +124,7 @@ else {
# Example usage of Write-LogHybrid # Example usage of Write-LogHybrid
Write-LogHybrid -Message "This is a test log message. Write-log2 does exist" -Level "Info" -TaskCategory "TestCategory" -LogToEvent:$true Write-LogHybrid -Message "This is a test log message. Write-log2 does exist" -Level "Info" -TaskCategory "TestCategory" -LogToEvent:$true
Write-Log -Message "This is a test log message. Write-log4444 does exist" -Level "Info" -TaskCategory "TestCategory" -LogToEvent:$true
function Install-SVSMSP { function Install-SVSMSP {
@@ -178,7 +183,6 @@ function Install-SVSMSP {
[string]$ApiSecretKey = "YOUR_API_SECRET_HERE" [string]$ApiSecretKey = "YOUR_API_SECRET_HERE"
) )
# Helper function: Perform Cleanup
function Perform-Cleanup { function Perform-Cleanup {
Write-LogHybrid -Message "Cleanup mode enabled. Starting cleanup process..." -Level "Info" -LogToEvent -EventID 1502 Write-LogHybrid -Message "Cleanup mode enabled. Starting cleanup process..." -Level "Info" -LogToEvent -EventID 1502
@@ -225,9 +229,8 @@ function Install-SVSMSP {
Write-LogHybrid -Message "Cleanup process completed successfully." -Level "Success" -LogToEvent -EventID 1510 Write-LogHybrid -Message "Cleanup process completed successfully." -Level "Success" -LogToEvent -EventID 1510
} }
# Helper function: Perform Toolkit Installation
function Perform-ToolkitInstallation { function Perform-ToolkitInstallation {
# Perform cleanup to remove old modules and repositories # Perform cleanup
Perform-Cleanup Perform-Cleanup
# Step 1: Set Execution Policy # Step 1: Set Execution Policy
@@ -282,7 +285,6 @@ function Install-SVSMSP {
Write-LogHybrid -Message "Toolkit installation process completed successfully." -Level "Success" -LogToEvent -EventID 1510 Write-LogHybrid -Message "Toolkit installation process completed successfully." -Level "Success" -LogToEvent -EventID 1510
} }
# Main Logic
Write-LogHybrid -Message "Install-SVSMSP function started." -Level "Info" -LogToEvent -EventID 1500 Write-LogHybrid -Message "Install-SVSMSP function started." -Level "Info" -LogToEvent -EventID 1500
if ($Cleanup) { if ($Cleanup) {
@@ -299,7 +301,7 @@ function Install-SVSMSP {
Perform-ToolkitInstallation Perform-ToolkitInstallation
} }
#Install-SVSMSP -InstallToolkit Install-SVSMSP -InstallToolkit
# ---------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------
@@ -316,21 +318,17 @@ function Get-N8nWebhookData {
[string]$AuthHeaderValue [string]$AuthHeaderValue
) )
# Define the URL and headers
$url = "https://automate.svstools.ca/webhook/svsmspkit" $url = "https://automate.svstools.ca/webhook/svsmspkit"
$headers = @{ $headers = @{
"SVSMSPKit" = $AuthHeaderValue "SVSMSPKit" = $AuthHeaderValue
} }
# Make the GET request to the N8N webhook
try { try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get $response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
Write-Host "Response received successfully:" -ForegroundColor Green Write-Host "Response received successfully:" -ForegroundColor Green
# Convert the JSON response to a PowerShell object
$data = $response $data = $response
# Map each field to a variable (if needed) # Map fields
$global:Comment_SVSmodule = $data._Comment_SVSmodule $global:Comment_SVSmodule = $data._Comment_SVSmodule
$global:ModuleName = $data.ModuleName $global:ModuleName = $data.ModuleName
$global:RepositoryURL = $data.RepositoryURL $global:RepositoryURL = $data.RepositoryURL
@@ -343,18 +341,8 @@ function Get-N8nWebhookData {
$global:ApiKey = $data.ApiKey $global:ApiKey = $data.ApiKey
$global:ApiSecretKey = $data.ApiSecretKey $global:ApiSecretKey = $data.ApiSecretKey
# Print the values
Write-Output "Module Name: $ModuleName"
Write-Output "Repository URL: $RepositoryURL"
Write-Output "Old Repo: $OldRepo"
Write-Output "New Repo: $NewRepo"
Write-Output "Commands To Check: $($CommandsToCheck -join ', ')"
Write-Output "Log File Path: $LogFilePath"
Write-Output "API URL: $ApiUrl"
Write-Output "API Key: $ApiKey"
Write-Output "API Secret Key: $ApiSecretKey"
# Return the parsed data object
return $data return $data
} }
catch { catch {
@@ -370,11 +358,11 @@ function GetHtmlContent {
@" @"
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>On-Boarding & Off-Boarding</title> <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> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@@ -383,18 +371,24 @@ function GetHtmlContent {
background-color: #1e1e1e; background-color: #1e1e1e;
color: #ffffff; color: #ffffff;
} }
.logo-container {
text-align: center;
padding: 20px;
background-color: #1e1e1e; /* Matches the background color */
}
.logo-container img {
max-width: 300px; /* Adjust size as needed */
height: auto;
}
.container { .container {
display: flex; display: flex;
height: 100vh; height: 100vh;
} }
.sidebar { .sidebar {
width: 200px; width: 200px;
background-color: #2e2e2e; background-color: #1e1e1e;
padding: 10px; padding: 10px;
} }
.sidebar button { .sidebar button {
display: block; display: block;
width: 100%; width: 100%;
@@ -408,30 +402,24 @@ function GetHtmlContent {
text-align: left; text-align: left;
transition: background-color 0.3s ease, transform 0.2s ease; transition: background-color 0.3s ease, transform 0.2s ease;
} }
.sidebar button.active { .sidebar button.active {
background-color: #007bff; background-color: #007bff;
} }
.sidebar button:hover { .sidebar button:hover {
background-color: #0056b3; background-color: #0056b3;
transform: scale(1.05); transform: scale(1.05);
} }
.content { .content {
flex: 1; flex: 1;
padding: 20px; padding: 20px;
overflow-y: auto; overflow-y: auto;
} }
.tab-content { .tab-content {
display: none; display: none;
} }
.tab-content.active { .tab-content.active {
display: block; display: block;
} }
.checkbox-group label { .checkbox-group label {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -439,7 +427,6 @@ function GetHtmlContent {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.password-input, .password-input,
.dropdown { .dropdown {
width: 30%; width: 30%;
@@ -450,12 +437,10 @@ function GetHtmlContent {
background-color: #2e2e2e; background-color: #2e2e2e;
color: white; color: white;
} }
.button-group { .button-group {
margin-top: 20px; margin-top: 20px;
text-align: right; text-align: right;
} }
.button-group button { .button-group button {
padding: 10px 20px; padding: 10px 20px;
margin-left: 10px; margin-left: 10px;
@@ -463,24 +448,20 @@ function GetHtmlContent {
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
} }
.install-button { .install-button {
background-color: #28a745; background-color: #28a745;
color: white; color: white;
} }
.install-button:disabled { .install-button:disabled {
background-color: #6c757d; background-color: #6c757d;
cursor: not-allowed; cursor: not-allowed;
} }
.exit-button { .exit-button {
background-color: #dc3545; background-color: #dc3545;
color: white; color: white;
} }
.log { .log {
width: 50%; width: 80%;
margin-top: 20px; margin-top: 20px;
padding: 10px; padding: 10px;
background-color: #333; background-color: #333;
@@ -489,20 +470,20 @@ function GetHtmlContent {
max-height: 300px; max-height: 300px;
overflow-y: auto; overflow-y: auto;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.container { .container {
flex-direction: column; flex-direction: column;
} }
.sidebar { .sidebar {
width: 100%; width: 100%;
} }
} }
</style> </style>
</head> </head>
<body> <body>
<div class="logo-container">
<img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Logo.png" alt="SVS Logo">
</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>
@@ -560,25 +541,21 @@ function GetHtmlContent {
<input type="checkbox" name="dattoRMMOption" value="exe"> <input type="checkbox" name="dattoRMMOption" value="exe">
Download.exe Download.exe
</label> </label>
<br> <br><br>
<br>
</div> </div>
</div> </div>
<div id="n8nPasswordContainer" style="display: none;"> <div id="n8nPasswordContainer" style="display: none;">
<label for="n8nPassword">Enter n8n Password:</label> <label for="n8nPassword">Enter n8n Password:</label><br>
<br>
<input type="password" id="n8nPassword" class="password-input" placeholder="Enter N8N Password"> <input type="password" id="n8nPassword" class="password-input" placeholder="Enter N8N Password">
</div> </div>
<br> <br>
<div id="DattoRMMContainer" style="display: none;"> <div id="DattoRMMContainer" style="display: none;">
<label for="dattoRmmDropdown">Select a Datto RMM site:</label> <label for="dattoRmmDropdown">Select a Datto RMM site:</label><br>
<br>
<select id="dattoRmmDropdown" class="dropdown"> <select id="dattoRmmDropdown" class="dropdown">
<option value="">Fetching sites...</option> <option value="">Fetching sites...</option>
$siteOptions $siteOptions
</select> </select>
</div> </div>
<div class="button-group"> <div class="button-group">
<button class="install-button" id="fetchSitesButton" onclick="fetchSites()" disabled>Fetch <button class="install-button" id="fetchSitesButton" onclick="fetchSites()" disabled>Fetch
Sites</button> Sites</button>
@@ -591,6 +568,7 @@ function GetHtmlContent {
</div> </div>
</div> </div>
</div> </div>
<script> <script>
function toggleOnboardCheckboxes(selectedCheckbox) { function toggleOnboardCheckboxes(selectedCheckbox) {
const checkboxes = document.querySelectorAll('#onboardTab input[type="checkbox"]'); const checkboxes = document.querySelectorAll('#onboardTab input[type="checkbox"]');
@@ -605,7 +583,6 @@ function GetHtmlContent {
} }
}); });
// Check if "Select All" is checked and handle the Datto RMM-specific logic
if (dattoRMMCheckbox.checked) { if (dattoRMMCheckbox.checked) {
optionsContainer.style.display = 'block'; optionsContainer.style.display = 'block';
n8nPasswordContainer.style.display = 'block'; n8nPasswordContainer.style.display = 'block';
@@ -621,16 +598,11 @@ function GetHtmlContent {
const checkbox = document.getElementById('installDattoRMMCheckbox'); const checkbox = document.getElementById('installDattoRMMCheckbox');
const optionsContainer = document.getElementById('dattoRMMOptionsContainer'); const optionsContainer = document.getElementById('dattoRMMOptionsContainer');
const n8nPasswordContainer = document.getElementById('n8nPasswordContainer'); const n8nPasswordContainer = document.getElementById('n8nPasswordContainer');
const DattoRMMContainer = document.getElementById('DattoRMMContainer') const DattoRMMContainer = document.getElementById('DattoRMMContainer');
// Show or hide the Datto RMM options
optionsContainer.style.display = checkbox.checked ? 'block' : 'none'; optionsContainer.style.display = checkbox.checked ? 'block' : 'none';
// Show or hide the password field
n8nPasswordContainer.style.display = checkbox.checked ? 'block' : 'none'; n8nPasswordContainer.style.display = checkbox.checked ? 'block' : 'none';
DattoRMMContainer.style.display = checkbox.checked ? 'block' : 'none';
// Show or Hide the DattoRMM site list
DattoRMMContainer.style.display = checkbox.checked ? 'block' : 'none'
} }
const tabButtons = document.querySelectorAll('.tab-button'); const tabButtons = document.querySelectorAll('.tab-button');
@@ -668,7 +640,6 @@ function GetHtmlContent {
try { try {
appendLog("Fetching sites...", "yellow"); appendLog("Fetching sites...", "yellow");
// Example fetch request (update URL and payload as needed)
const response = await fetch('/getn8npw', { const response = await fetch('/getn8npw', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@@ -679,24 +650,20 @@ function GetHtmlContent {
throw new Error('Failed to fetch sites. Please try again.'); throw new Error('Failed to fetch sites. Please try again.');
} }
// Parse the JSON response into an array (or object)
const sites = await response.json(); const sites = await response.json();
// Clear out existing options in the dropdown
dropdown.innerHTML = ''; dropdown.innerHTML = '';
// Populate dropdown with new options
sites.forEach(site => { sites.forEach(site => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = site.UID; // or whatever property holds the site ID // Adjust property names based on your actual data
option.textContent = site.Name; // or the relevant display name option.value = site.UID;
option.textContent = site.Name;
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
appendLog("Sites fetched successfully, please select a site!", "green"); appendLog("Sites fetched successfully, please select a site!", "green");
}
} catch (error) { catch (error) {
// Handle errors and display a fallback option
dropdown.innerHTML = '<option value="">Fetching sites failed</option>'; dropdown.innerHTML = '<option value="">Fetching sites failed</option>';
appendLog('Error:' + error.message, "red"); appendLog('Error:' + error.message, "red");
} }
@@ -707,7 +674,7 @@ function GetHtmlContent {
const UID = dropdown.options[dropdown.selectedIndex].value; const UID = dropdown.options[dropdown.selectedIndex].value;
const Name = dropdown.options[dropdown.selectedIndex].text; const Name = dropdown.options[dropdown.selectedIndex].text;
const installDattoRMM = document.querySelector('input[name="installDattoRMM"]') const installDattoRMM = document.querySelector('input[name="installDattoRMM"]');
const installCyberQP = document.querySelector('input[name="installCyberQP"]'); const installCyberQP = document.querySelector('input[name="installCyberQP"]');
const installSplashtop = document.querySelector('input[name="installSplashtop"]'); const installSplashtop = document.querySelector('input[name="installSplashtop"]');
const installSVSHelpDesk = document.querySelector('input[name="installSVSHelpDesk"]'); const installSVSHelpDesk = document.querySelector('input[name="installSVSHelpDesk"]');
@@ -717,15 +684,11 @@ function GetHtmlContent {
if (installDattoRMM.checked) { if (installDattoRMM.checked) {
const DattoRMMCheckbox = document.querySelectorAll('input[name="dattoRMMOption"]:checked'); const DattoRMMCheckbox = document.querySelectorAll('input[name="dattoRMMOption"]:checked');
appendLog("Installing selected site RMM...", "cyan"); appendLog("Installing selected site RMM...", "cyan");
const checkedValues = Array.from(DattoRMMCheckbox).map(c => c.value); const checkedValues = Array.from(DattoRMMCheckbox).map(c => c.value);
// Initialize the PowerShell command
let installRMMCommand = 'Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey'; let installRMMCommand = 'Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey';
// Check individual selections and append flags to the command
if (checkedValues.includes('inputVar')) { if (checkedValues.includes('inputVar')) {
installRMMCommand += ' -PushSiteVars'; installRMMCommand += ' -PushSiteVars';
} }
@@ -736,83 +699,98 @@ function GetHtmlContent {
installRMMCommand += ' -SaveCopy'; installRMMCommand += ' -SaveCopy';
} }
const response = fetch('/installrmm', { fetch('/installrmm', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({ installRMMCommand, UID, Name })
installRMMCommand,
UID,
Name
})
}); });
} }
if (installCyberQP.checked) {
fetch('/installCyberQP', { method: 'GET' }) if (installCyberQP.checked) {
fetch('/installCyberQP', { method: 'GET' });
appendLog("Installing CyberQP", "cyan"); appendLog("Installing CyberQP", "cyan");
} }
if (installSplashtop.checked) { if (installSplashtop.checked) {
fetch('/installSplashtop', { method: 'GET' });
appendLog("Installing Splashtop", "cyan"); appendLog("Installing Splashtop", "cyan");
} }
if (installSVSHelpDesk.checked) { if (installSVSHelpDesk.checked) {
fetch('/installSVSHelpDesk', { method: 'GET' });
appendLog("Installing SVSHelpdesk", "cyan"); appendLog("Installing SVSHelpdesk", "cyan");
} }
if (installSVSWatchtower.checked) { if (installSVSWatchtower.checked) {
fetch('/installSVSWatchtower', { method: 'GET' });
appendLog("Installing SVSWatchtower", "cyan"); appendLog("Installing SVSWatchtower", "cyan");
} }
if (installThreatLocker.checked) { if (installThreatLocker.checked) {
fetch('/installThreatLocker', { method: 'GET' });
appendLog("Installing ThreatLocker", "cyan"); appendLog("Installing ThreatLocker", "cyan");
} }
if (installRocketCyber.checked) { if (installRocketCyber.checked) {
fetch('/installRocketCyber', { method: 'GET' });
appendLog("Installing RocketCyber", "cyan"); appendLog("Installing RocketCyber", "cyan");
} }
} }
function endSession() { function endSession() {
appendLog("Session ended. Closing application...", "yellow"); appendLog("Session ended. Closing application...", "yellow");
fetch('/quit', { method: 'GET' }) fetch('/quit', { method: 'GET' })
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to end session'); throw new Error('Failed to end session');
} }
// Optionally, close the window or do something else window.close();
window.close()
}) })
.catch(error => { .catch(error => {
appendLog("Error ending session: " + error.message, "red"); appendLog("Error ending session: " + error.message, "red");
}); });
} }
function appendLog(message, color = "white") { function appendLog(message, color = "white") {
const log = document.createElement('p'); const log = document.createElement('p');
log.style.color = color; log.style.color = color;
log.textContent = message; log.textContent = message;
logArea.appendChild(log); document.getElementById('logArea').appendChild(log);
} }
// -------------------------------------------------------------------
// 3) POLL THE SERVER LOGS (NEW)
// -------------------------------------------------------------------
let lastLogCount = 0;
async function fetchLogs() {
try {
const resp = await fetch('/getLogs');
if (!resp.ok) return;
const logs = await resp.json(); // array of {Timestamp, Level, Message}
// Append only new messages
for (let i = lastLogCount; i < logs.length; i++) {
// We'll display each new line in "white" or a color of your choice
appendLog(logs[i].Message, "white");
}
lastLogCount = logs.length;
} catch (err) {
console.error("Error fetching logs:", err);
}
}
// Poll logs every 3 seconds (feel free to adjust)
setInterval(fetchLogs, 3000);
// -------------------------------------------------------------------
</script> </script>
</body> </body>
</html> </html>
"@ "@
} }
# Save and launch the HTML # Save and launch the HTML
Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/" Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:8081/"
try { try {
while ($listener.IsListening) { while ($listener.IsListening) {
# Wait for an incoming request
$context = $listener.GetContext() $context = $listener.GetContext()
$request = $context.Request $request = $context.Request
$response = $context.Response $response = $context.Response
@@ -820,7 +798,6 @@ try {
switch ($request.Url.AbsolutePath) { switch ($request.Url.AbsolutePath) {
"/" { "/" {
# Serve the main HTML
$htmlContent = GetHtmlContent $htmlContent = GetHtmlContent
$buffer = [System.Text.Encoding]::UTF8.GetBytes($htmlContent) $buffer = [System.Text.Encoding]::UTF8.GetBytes($htmlContent)
$response.ContentType = "text/html" $response.ContentType = "text/html"
@@ -831,17 +808,13 @@ try {
"/getn8npw" { "/getn8npw" {
if ($request.HttpMethod -eq "POST") { if ($request.HttpMethod -eq "POST") {
# Parse the received JSON
$bodyStream = New-Object IO.StreamReader $request.InputStream $bodyStream = New-Object IO.StreamReader $request.InputStream
$body = $bodyStream.ReadToEnd() $body = $bodyStream.ReadToEnd()
$data = ConvertFrom-Json $body $data = ConvertFrom-Json $body
$password = $data.password $password = $data.password
Get-N8nWebhookData -AuthHeaderValue $password Get-N8nWebhookData -AuthHeaderValue $password
# Fetch the list of Datto RMM client sites
$sites = Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey -FetchSitesOnly $sites = Install-DattoRMM -ApiUrl $ApiUrl -ApiKey $ApiKey -ApiSecretKey $ApiSecretKey -FetchSitesOnly
if (-not $sites) { if (-not $sites) {
Write-Host "No sites returned. Please check the API." -ForegroundColor Red Write-Host "No sites returned. Please check the API." -ForegroundColor Red
$response.StatusCode = 500 $response.StatusCode = 500
@@ -850,15 +823,10 @@ try {
$response.OutputStream.Close() $response.OutputStream.Close()
continue continue
} }
# Convert the array (or object) in $sites to JSON
$responseData = $sites | ConvertTo-Json $responseData = $sites | ConvertTo-Json
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseData) $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseData)
# Set the response headers
$response.ContentType = "application/json" $response.ContentType = "application/json"
$response.ContentLength64 = $buffer.Length $response.ContentLength64 = $buffer.Length
$response.OutputStream.Write($buffer, 0, $buffer.Length) $response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.OutputStream.Close() $response.OutputStream.Close()
} }
@@ -871,7 +839,6 @@ try {
$selectedSite = ConvertFrom-Json $body $selectedSite = ConvertFrom-Json $body
Invoke-Expression $selectedSite.installRMMCommand Invoke-Expression $selectedSite.installRMMCommand
# Return a simple success response
$responseString = "RMM install triggered." $responseString = "RMM install triggered."
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString) $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$response.ContentType = "text/plain" $response.ContentType = "text/plain"
@@ -885,7 +852,6 @@ try {
if ($request.HttpMethod -eq "GET") { if ($request.HttpMethod -eq "GET") {
Install-CyberQP Install-CyberQP
} }
# Return something
$responseString = "Install CyberQP triggered." $responseString = "Install CyberQP triggered."
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString) $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$response.ContentType = "text/plain" $response.ContentType = "text/plain"
@@ -954,41 +920,50 @@ try {
$response.OutputStream.Close() $response.OutputStream.Close()
} }
# ----------------------------------------------------------------
# 4) NEW ROUTE: /getLogs
# Returns $Global:LogCache as JSON for the polling function
# ----------------------------------------------------------------
"/getLogs" {
if ($request.HttpMethod -eq "GET") {
$jsonLogs = $Global:LogCache | ConvertTo-Json
$logBuffer = [System.Text.Encoding]::UTF8.GetBytes($jsonLogs)
$response.ContentType = "application/json"
$response.ContentLength64 = $logBuffer.Length
$response.OutputStream.Write($logBuffer, 0, $logBuffer.Length)
$response.OutputStream.Close()
}
}
"/quit" { "/quit" {
if ($request.HttpMethod -eq "GET") { if ($request.HttpMethod -eq "GET") {
# Return a "shutting down" message $responseString = "Server shutting down."
$responseString = "Server shutting down." $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
$buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString) $response.ContentType = "text/plain"
$response.ContentType = "text/plain" $response.ContentLength64 = $buffer.Length
$response.ContentLength64 = $buffer.Length $response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.OutputStream.Write($buffer, 0, $buffer.Length) $response.OutputStream.Close()
$response.OutputStream.Close() Write-Host $responseString
Write-Host $responseString $listener.stop()
$listener.stop() break
break # exit the while loop
} }
} }
default { default {
# Handle unknown routes
$response.StatusCode = 404 $response.StatusCode = 404
$response.StatusDescription = "Not Found" $response.StatusDescription = "Not Found"
$buffer = [System.Text.Encoding]::UTF8.GetBytes("404 - Not Found") $buffer = [System.Text.Encoding]::UTF8.GetBytes("404 - Not Found")
$response.OutputStream.Write($buffer, 0, $buffer.Length) $response.OutputStream.Write($buffer, 0, $buffer.Length)
$response.OutputStream.Close() $response.OutputStream.Close()
} }
} # end switch }
} # end while }
} }
catch { catch {
Write-Host "Error: $($_.Exception.Message)" Write-Host "Error: $($_.Exception.Message)"
} }
finally { finally {
$listener.Stop() $listener.Stop()
$listener.Close() $listener.Close()
} }