diff --git a/StackMonkey.ps1 b/StackMonkey.ps1 index 72d0ebd..533f39c 100644 --- a/StackMonkey.ps1 +++ b/StackMonkey.ps1 @@ -11,6 +11,10 @@ # Listening port for HTTP UI $Port = 8082 +# Configurable endpoints +$Global:DattoWebhookUrl = 'https://automate.svstools.ca/webhook/svsmspkit' + + # Define every task once here: # Id → checkbox HTML `id` # Name → URL path (`/Name`) @@ -261,31 +265,35 @@ function Install-SVSMSP { function Handle-FetchSites { param($Context) - # 1) Read incoming JSON - $raw = (New-Object IO.StreamReader $Context.Request.InputStream).ReadToEnd() + # 1) Read incoming JSON (using block auto-disposes the reader) + using ($reader = [IO.StreamReader]::new($Context.Request.InputStream)) { + $raw = $reader.ReadToEnd() + } + try { $pw = (ConvertFrom-Json $raw).password + if (-not $pw) { throw "Missing `password` field" } } catch { Write-LogHybrid "Invalid JSON in /getpw payload: $($_.Exception.Message)" "Error" "FetchSites" - returnRespondEmpty $Context + returnRespondEmpty $Context 400 return } # 2) Fetch your Datto API creds from the webhook Write-LogHybrid "Calling webhook for Datto credentials…" "Info" "FetchSites" try { - $hdr = @{ "SVSMSPKit" = $pw } - $resp = Invoke-RestMethod -Uri "https://automate.svstools.ca/webhook/svsmspkit" ` - -Headers $hdr -Method Get + $hdr = @{ "SVSMSPKit" = $pw } + $resp = Invoke-RestMethod -Uri $Global:DattoWebhookUrl -Headers $hdr -Method GET # store for later RMM calls $Global:ApiUrl = $resp.ApiUrl $Global:ApiKey = $resp.ApiKey $Global:ApiSecretKey = $resp.ApiSecretKey + Write-LogHybrid "Fetched and stored API credentials." "Success" "FetchSites" } catch { Write-LogHybrid "Webhook call failed: $($_.Exception.Message)" "Error" "FetchSites" -LogToEvent - returnRespondEmpty $Context + returnRespondEmpty $Context 403 return } @@ -295,16 +303,16 @@ function Handle-FetchSites { $securePublic = ConvertTo-SecureString 'public' -AsPlainText -Force $creds = New-Object System.Management.Automation.PSCredential('public-client',$securePublic) $tokenResp = Invoke-RestMethod ` - -Uri "$ApiUrl/auth/oauth/token" ` - -Credential $creds ` - -Method Post ` - -ContentType 'application/x-www-form-urlencoded' ` - -Body "grant_type=password&username=$ApiKey&password=$ApiSecretKey" + -Uri "$Global:ApiUrl/auth/oauth/token" ` + -Credential $creds ` + -Method Post ` + -ContentType 'application/x-www-form-urlencoded' ` + -Body "grant_type=password&username=$Global:ApiKey&password=$Global:ApiSecretKey" $token = $tokenResp.access_token Write-LogHybrid "OAuth token acquired." "Success" "FetchSites" } catch { Write-LogHybrid "OAuth request failed: $($_.Exception.Message)" "Error" "FetchSites" - returnRespondEmpty $Context + returnRespondEmpty $Context 500 return } @@ -312,17 +320,18 @@ function Handle-FetchSites { Write-LogHybrid "Fetching Datto RMM site list" "Info" "FetchSites" try { $hdr = @{ Authorization = "Bearer $token" } - $sitesResp = Invoke-RestMethod -Uri "$ApiUrl/api/v2/account/sites" ` + $sitesResp = Invoke-RestMethod -Uri "$Global:ApiUrl/api/v2/account/sites" ` -Method Get ` -Headers $hdr ` -ContentType 'application/json' + $siteList = $sitesResp.sites | ForEach-Object { [PSCustomObject]@{ Name = $_.name; UID = $_.uid } } - Write-LogHybrid "Site list retrieved (${siteList.Count} sites)." "Success" "FetchSites" + Write-LogHybrid "Site list retrieved ($($siteList.Count) sites)." "Success" "FetchSites" } catch { Write-LogHybrid "Failed to fetch site list: $($_.Exception.Message)" "Error" "FetchSites" - returnRespondEmpty $Context + returnRespondEmpty $Context 500 return } @@ -339,16 +348,25 @@ function Handle-FetchSites { # Helper function to consistently return an empty JSON array function returnRespondEmpty { - param($Context) + param( + [Parameter(Mandatory)][object]$Context, + [Parameter(Mandatory)][ValidateRange(100,599)][int]$StatusCode = 500 + ) + # Always return an empty JSON array body $empty = [Text.Encoding]::UTF8.GetBytes("[]") - $Context.Response.StatusCode = 500 + + # Set the desired status code and headers + $Context.Response.StatusCode = $StatusCode $Context.Response.ContentType = 'application/json' $Context.Response.ContentLength64 = $empty.Length + + # Write and close $Context.Response.OutputStream.Write($empty, 0, $empty.Length) $Context.Response.OutputStream.Close() } + # On-boarding handlers function Handle-SetSVSPowerPlan { param($Context)