308 lines
9.4 KiB
PowerShell
308 lines
9.4 KiB
PowerShell
param(
|
|
[string]$ServiceName = "ScreenJobBackend"
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
Add-Type -AssemblyName System.Drawing
|
|
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$controlScript = Join-Path $scriptDir "tray_service_control.ps1"
|
|
$logsDir = Join-Path $scriptDir "screenjob_runs\service"
|
|
$defaultHost = "127.0.0.1"
|
|
$defaultPort = "8787"
|
|
|
|
function Read-EnvConfig {
|
|
param([string]$EnvFilePath)
|
|
$result = @{}
|
|
if (-not (Test-Path -LiteralPath $EnvFilePath)) {
|
|
return $result
|
|
}
|
|
|
|
foreach ($line in Get-Content -Path $EnvFilePath) {
|
|
$trimmed = $line.Trim()
|
|
if ($trimmed.Length -eq 0 -or $trimmed.StartsWith("#")) {
|
|
continue
|
|
}
|
|
$parts = $trimmed.Split("=", 2)
|
|
if ($parts.Count -eq 2) {
|
|
$key = $parts[0].Trim()
|
|
$value = $parts[1].Trim()
|
|
if (($value.StartsWith('"') -and $value.EndsWith('"')) -or ($value.StartsWith("'") -and $value.EndsWith("'"))) {
|
|
$value = $value.Substring(1, $value.Length - 2)
|
|
}
|
|
$result[$key] = $value
|
|
}
|
|
}
|
|
return $result
|
|
}
|
|
|
|
function Get-ServiceStatusSafe {
|
|
param([string]$Name)
|
|
try {
|
|
$svc = Get-Service -Name $Name -ErrorAction Stop
|
|
return $svc.Status.ToString()
|
|
} catch {
|
|
return "NotInstalled"
|
|
}
|
|
}
|
|
|
|
function Invoke-ServiceActionElevated {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Action,
|
|
[Parameter(Mandatory = $true)][string]$Name
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $controlScript)) {
|
|
[System.Windows.Forms.MessageBox]::Show(
|
|
"Missing control script: $controlScript",
|
|
"ScreenJob Tray",
|
|
[System.Windows.Forms.MessageBoxButtons]::OK,
|
|
[System.Windows.Forms.MessageBoxIcon]::Error
|
|
) | Out-Null
|
|
return
|
|
}
|
|
|
|
$argList = @(
|
|
"-NoProfile",
|
|
"-ExecutionPolicy", "Bypass",
|
|
"-File", "`"$controlScript`"",
|
|
"-Action", $Action,
|
|
"-ServiceName", $Name
|
|
)
|
|
|
|
try {
|
|
Start-Process -FilePath "powershell.exe" -ArgumentList $argList -Verb RunAs -WindowStyle Hidden | Out-Null
|
|
} catch {
|
|
# User canceled UAC prompt or launch failed.
|
|
}
|
|
}
|
|
|
|
function Get-DashboardUrl {
|
|
$envFile = Join-Path $scriptDir ".env"
|
|
$envVars = Read-EnvConfig -EnvFilePath $envFile
|
|
|
|
$dashboardHost = $defaultHost
|
|
$dashboardPort = $defaultPort
|
|
|
|
if ($envVars.ContainsKey("SCREENJOB_HOST") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_HOST"])) {
|
|
$dashboardHost = $envVars["SCREENJOB_HOST"]
|
|
}
|
|
if ($envVars.ContainsKey("SCREENJOB_PORT") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_PORT"])) {
|
|
$dashboardPort = $envVars["SCREENJOB_PORT"]
|
|
}
|
|
|
|
$connectHost = Resolve-ConnectHost -ConfiguredHost $dashboardHost
|
|
return "http://{0}:{1}/" -f $connectHost, $dashboardPort
|
|
}
|
|
|
|
function Resolve-ConnectHost {
|
|
param([string]$ConfiguredHost)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ConfiguredHost)) {
|
|
return "127.0.0.1"
|
|
}
|
|
|
|
switch ($ConfiguredHost.Trim().ToLowerInvariant()) {
|
|
"0.0.0.0" { return "127.0.0.1" }
|
|
"::" { return "127.0.0.1" }
|
|
"*" { return "127.0.0.1" }
|
|
default { return $ConfiguredHost }
|
|
}
|
|
}
|
|
|
|
function Get-HealthCheckHosts {
|
|
param([string]$ConfiguredHost)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ConfiguredHost)) {
|
|
return @("127.0.0.1", "localhost")
|
|
}
|
|
|
|
$normalized = $ConfiguredHost.Trim().ToLowerInvariant()
|
|
switch ($normalized) {
|
|
"0.0.0.0" { return @("127.0.0.1", "localhost", "::1") }
|
|
"::" { return @("127.0.0.1", "localhost", "::1") }
|
|
"*" { return @("127.0.0.1", "localhost", "::1") }
|
|
default { return @($ConfiguredHost) }
|
|
}
|
|
}
|
|
|
|
function Test-TcpEndpoint {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$HostName,
|
|
[Parameter(Mandatory = $true)][int]$Port,
|
|
[int]$TimeoutMs = 1200
|
|
)
|
|
|
|
$client = New-Object System.Net.Sockets.TcpClient
|
|
try {
|
|
$async = $client.BeginConnect($HostName, $Port, $null, $null)
|
|
$connected = $async.AsyncWaitHandle.WaitOne($TimeoutMs, $false)
|
|
if (-not $connected) {
|
|
return $false
|
|
}
|
|
$client.EndConnect($async) | Out-Null
|
|
return $true
|
|
} catch {
|
|
return $false
|
|
} finally {
|
|
$client.Dispose()
|
|
}
|
|
}
|
|
|
|
function Get-BackendReachability {
|
|
$envFile = Join-Path $scriptDir ".env"
|
|
$envVars = Read-EnvConfig -EnvFilePath $envFile
|
|
$configuredHost = $defaultHost
|
|
$configuredPort = $defaultPort
|
|
|
|
if ($envVars.ContainsKey("SCREENJOB_HOST") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_HOST"])) {
|
|
$configuredHost = $envVars["SCREENJOB_HOST"]
|
|
}
|
|
if ($envVars.ContainsKey("SCREENJOB_PORT") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_PORT"])) {
|
|
$configuredPort = $envVars["SCREENJOB_PORT"]
|
|
}
|
|
|
|
$portNumber = 8787
|
|
[void][int]::TryParse([string]$configuredPort, [ref]$portNumber)
|
|
$hostsToTry = Get-HealthCheckHosts -ConfiguredHost $configuredHost
|
|
|
|
foreach ($candidateHost in $hostsToTry) {
|
|
if (Test-TcpEndpoint -HostName $candidateHost -Port $portNumber) {
|
|
return $true
|
|
}
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
function Update-TrayState {
|
|
param(
|
|
[System.Windows.Forms.NotifyIcon]$NotifyIcon,
|
|
[System.Windows.Forms.ToolStripMenuItem]$StatusItem,
|
|
[string]$Name
|
|
)
|
|
|
|
$status = Get-ServiceStatusSafe -Name $Name
|
|
$isBackendReachable = Get-BackendReachability
|
|
|
|
$displayStatus = $status
|
|
if ($status -eq "Running" -and -not $isBackendReachable) {
|
|
$displayStatus = "Running (Backend Down)"
|
|
} elseif ($status -eq "Stopped" -and $isBackendReachable) {
|
|
$displayStatus = "Stopped (Backend Up)"
|
|
} elseif ($status -eq "NotInstalled" -and $isBackendReachable) {
|
|
$displayStatus = "NotInstalled (Backend Up)"
|
|
}
|
|
|
|
$StatusItem.Text = "Status: $displayStatus"
|
|
|
|
switch ($displayStatus) {
|
|
"Running" {
|
|
$NotifyIcon.Icon = [System.Drawing.SystemIcons]::Information
|
|
}
|
|
"Stopped" {
|
|
$NotifyIcon.Icon = [System.Drawing.SystemIcons]::Warning
|
|
}
|
|
default {
|
|
$NotifyIcon.Icon = [System.Drawing.SystemIcons]::Error
|
|
}
|
|
}
|
|
|
|
$tooltip = "ScreenJob Backend: $displayStatus"
|
|
if ($tooltip.Length -gt 63) {
|
|
$tooltip = $tooltip.Substring(0, 63)
|
|
}
|
|
$NotifyIcon.Text = $tooltip
|
|
}
|
|
|
|
$appContext = New-Object System.Windows.Forms.ApplicationContext
|
|
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
$notifyIcon.Visible = $false
|
|
|
|
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
$statusItem = New-Object System.Windows.Forms.ToolStripMenuItem "Status: Unknown"
|
|
$statusItem.Enabled = $false
|
|
|
|
$refreshItem = New-Object System.Windows.Forms.ToolStripMenuItem "Refresh Status"
|
|
$refreshItem.Add_Click({
|
|
Update-TrayState -NotifyIcon $notifyIcon -StatusItem $statusItem -Name $ServiceName
|
|
})
|
|
|
|
$startItem = New-Object System.Windows.Forms.ToolStripMenuItem "Start Service (Admin)"
|
|
$startItem.Add_Click({
|
|
Invoke-ServiceActionElevated -Action "start" -Name $ServiceName
|
|
})
|
|
|
|
$stopItem = New-Object System.Windows.Forms.ToolStripMenuItem "Stop Service (Admin)"
|
|
$stopItem.Add_Click({
|
|
Invoke-ServiceActionElevated -Action "stop" -Name $ServiceName
|
|
})
|
|
|
|
$restartItem = New-Object System.Windows.Forms.ToolStripMenuItem "Restart Service (Admin)"
|
|
$restartItem.Add_Click({
|
|
Invoke-ServiceActionElevated -Action "restart" -Name $ServiceName
|
|
})
|
|
|
|
$dashboardItem = New-Object System.Windows.Forms.ToolStripMenuItem "Open Dashboard"
|
|
$dashboardItem.Add_Click({
|
|
$url = Get-DashboardUrl
|
|
Start-Process $url | Out-Null
|
|
})
|
|
|
|
$logsItem = New-Object System.Windows.Forms.ToolStripMenuItem "Open Service Logs"
|
|
$logsItem.Add_Click({
|
|
if (-not (Test-Path -LiteralPath $logsDir)) {
|
|
New-Item -ItemType Directory -Path $logsDir -Force | Out-Null
|
|
}
|
|
Start-Process explorer.exe $logsDir | Out-Null
|
|
})
|
|
|
|
$openFolderItem = New-Object System.Windows.Forms.ToolStripMenuItem "Open Project Folder"
|
|
$openFolderItem.Add_Click({
|
|
Start-Process explorer.exe $scriptDir | Out-Null
|
|
})
|
|
|
|
$exitItem = New-Object System.Windows.Forms.ToolStripMenuItem "Exit Tray"
|
|
$exitItem.Add_Click({
|
|
$refreshTimer.Stop()
|
|
$notifyIcon.Visible = $false
|
|
$notifyIcon.Dispose()
|
|
$menu.Dispose()
|
|
$appContext.ExitThread()
|
|
})
|
|
|
|
[void]$menu.Items.Add($statusItem)
|
|
[void]$menu.Items.Add($refreshItem)
|
|
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
|
|
[void]$menu.Items.Add($startItem)
|
|
[void]$menu.Items.Add($stopItem)
|
|
[void]$menu.Items.Add($restartItem)
|
|
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
|
|
[void]$menu.Items.Add($dashboardItem)
|
|
[void]$menu.Items.Add($logsItem)
|
|
[void]$menu.Items.Add($openFolderItem)
|
|
[void]$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator))
|
|
[void]$menu.Items.Add($exitItem)
|
|
|
|
$notifyIcon.ContextMenuStrip = $menu
|
|
$notifyIcon.Visible = $true
|
|
|
|
$notifyIcon.Add_DoubleClick({
|
|
$url = Get-DashboardUrl
|
|
Start-Process $url | Out-Null
|
|
})
|
|
|
|
$refreshTimer = New-Object System.Windows.Forms.Timer
|
|
$refreshTimer.Interval = 5000
|
|
$refreshTimer.Add_Tick({
|
|
Update-TrayState -NotifyIcon $notifyIcon -StatusItem $statusItem -Name $ServiceName
|
|
})
|
|
|
|
Update-TrayState -NotifyIcon $notifyIcon -StatusItem $statusItem -Name $ServiceName
|
|
$refreshTimer.Start()
|
|
|
|
[System.Windows.Forms.Application]::Run($appContext)
|