Files
SAMY/samy.ps1
2026-01-31 22:10:45 -05:00

196 lines
6.5 KiB
PowerShell

<#
.SYNOPSIS
Script Automation Monkey (SAMY) ...
.NOTES
Full documentation: https://git.svstools.ca/.../docs/SAMY.help.md
#>
#region Safely bypass Restricted Execution Policy
if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage' -or
(Get-ExecutionPolicy) -eq 'Restricted') {
Write-Host "[Info] Relaunching with ExecutionPolicy Bypass..." -ForegroundColor Yellow
# Build token list (NO manual quoting)
$argList = foreach ($a in $args) { [string]$a }
if ($PSCommandPath) {
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$PSCommandPath" @argList
}
else {
# Download -> create ScriptBlock -> INVOKE it with @args so $args survives the relaunch
$bootstrap = "& { `$sb = [ScriptBlock]::Create((Invoke-WebRequest 'https://samybeta.svstools.ca' -UseBasicParsing).Content); & `$sb @args }"
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command $bootstrap @argList
# temp
Write-Host "[Debug] Script saw args: $($args -join ' | ')" -ForegroundColor Magenta
}
exit
}
#endregion Safely bypass Restricted Execution Policy
# TLS and silent install defaults
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue'
$ConfirmPreference = 'None'
function Initialize-SamyConfig {
[CmdletBinding()]
param()
# Listening port for HTTP UI
$Script:Port = 8082
# Endpoints
$Global:DattoWebhookUrl = 'https://bananas.svstools.ca/dattormm'
# Hint text shown in UI
$Script:SamyHintText = ""
# IMPORTANT: ui.ps1 expects $Script:SamyBranch (not SamyGitBranch)
if ([string]::IsNullOrWhiteSpace($Script:SamyBranch)) {
$Script:SamyBranch = $Script:SamyGitBranch
}
}
#region Remote chunk loader
function Import-SamyChunk {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$Url,
[Parameter(Mandatory)][string]$Name
)
try {
$content = (Invoke-WebRequest -UseBasicParsing -Uri $Url -ErrorAction Stop).Content
if ([string]::IsNullOrWhiteSpace($content)) {
throw "Downloaded content was empty."
}
#Write-Host ("[Success] {0}" -f $Name) -ForegroundColor Green
return $content
}
catch {
$msg = $_.Exception.Message
if ([string]::IsNullOrWhiteSpace($msg)) { $msg = "$_" }
Write-Host ("[Failed] {0}: {1}" -f $Name, $msg) -ForegroundColor Red
throw
}
}
# Single source of truth for where chunks live
# (Matches your current inline values)
$Script:SamyGitURL = 'https://git.svstools.ca/SVS_Public_Repo/SAMY/raw/branch'
$Script:SamyGitBranch = 'beta' # 'main' or 'beta'
$Script:SamyGitRepo = "$Script:SamyGitURL/$Script:SamyGitBranch"
Initialize-SamyConfig
$Script:SamyTopLogoUrl = "$Script:SamyGitRepo/SVS_logo.svg?raw=1"
$Script:SamyBgLogoUrl = "$Script:SamyGitRepo/SAMY.png?raw=1"
$Script:SamyFaviconUrl = "$Script:SamyGitRepo/SVS_Favicon.ico?raw=1"
$Script:SamyCssUrl = "$Script:SamyGitRepo/samy.css?raw=1"
$Script:SamyJsUrl = "$Script:SamyGitRepo/samy.js?raw=1"
$Script:SamyHtmlUrl = "$Script:SamyGitRepo/samy.html?raw=1"
$Script:SamyTasksUrl = "$Script:SamyGitRepo/samy.tasks.json?raw=1"
$Script:ChunkBase = "$Script:SamyGitRepo/src"
# Load chunks (dependencies first)
$chunks = @(
# 1) Logging next: used everywhere
@{ Name = 'logging.fallback.ps1'; Url = "$Script:ChunkBase/logging.fallback.ps1?raw=1" },
# 2) Core helpers/utilities that others depend on
@{ Name = 'http.ps1'; Url = "$Script:ChunkBase/http.ps1?raw=1" },
# 3) Core data loaders / domain logic
@{ Name = 'tasks.ps1'; Url = "$Script:ChunkBase/tasks.ps1?raw=1" },
# 4) Integrations + installers
@{ Name = 'svsmsp.install.ps1'; Url = "$Script:ChunkBase/svsmsp.install.ps1?raw=1" },
@{ Name = 'integrations.datto.ps1';Url = "$Script:ChunkBase/integrations.datto.ps1?raw=1" },
# 5) UI renderer (Get-UIHtml depends on URLs + http helpers)
@{ Name = 'ui.ps1'; Url = "$Script:ChunkBase/ui.ps1?raw=1" },
# 6) Handlers (depend on tasks/helpers/logging)
@{ Name = 'handlers.datto.ps1'; Url = "$Script:ChunkBase/handlers.datto.ps1?raw=1" },
@{ Name = 'handlers.onboard.ps1'; Url = "$Script:ChunkBase/handlers.onboard.ps1?raw=1" },
@{ Name = 'handlers.offboard.ps1'; Url = "$Script:ChunkBase/handlers.offboard.ps1?raw=1" },
@{ Name = 'handlers.printers.ps1'; Url = "$Script:ChunkBase/handlers.printers.ps1?raw=1" },
# 7) Router and server last (depend on UI + handlers + http)
@{ Name = 'server.ps1'; Url = "$Script:ChunkBase/server.ps1?raw=1" },
# 8) Functions file can be early OR late, but safest is after config/logging.
@{ Name = 'samy.functions.ps1'; Url = "$Script:ChunkBase/samy.functions.ps1?raw=1" },
# 9) Entry point last
@{ Name = 'core.ps1'; Url = "$Script:ChunkBase/core.ps1?raw=1" }
)
foreach ($c in $chunks) {
try {
$content = Import-SamyChunk -Url $c.Url -Name $c.Name -ErrorAction Stop
. ([ScriptBlock]::Create($content))
Write-Host "[Success] $($c.Name)" -ForegroundColor Green
}
catch {
$msg = $_.Exception.Message
if ([string]::IsNullOrWhiteSpace($msg)) { $msg = "$_" }
Write-Host "[Failed] $($c.Name): $msg" -ForegroundColor Red
throw # or continue
}
}
if (-not (Get-Command Invoke-ScriptAutomationMonkey -ErrorAction SilentlyContinue)) {
throw "Bootstrap loaded chunks, but Invoke-ScriptAutomationMonkey was not found. Ensure src/core.ps1 defines it."
}
#endregion Remote chunk loader
#region Entry behavior (same intent as your original)
if ($MyInvocation.InvocationName -eq '.') {
# dot-sourced, don't invoke
return
}
elseif ($PSCommandPath) {
# script was saved and run directly
Invoke-ScriptAutomationMonkey @args
}
else {
# iwr | iex fallback
if ($args.Count -gt 0) {
$namedArgs = @{}
for ($i = 0; $i -lt $args.Count; $i++) {
if ($args[$i] -is [string] -and $args[$i].StartsWith('-')) {
$key = $args[$i].TrimStart('-')
$next = $args[$i + 1]
if ($next -and ($next -notlike '-*')) {
$namedArgs[$key] = $next
$i++
} else {
$namedArgs[$key] = $true
}
}
}
Invoke-ScriptAutomationMonkey @namedArgs
} else {
Invoke-ScriptAutomationMonkey
}
}
#endregion Entry behavior