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) { $result[$parts[0].Trim()] = $parts[1].Trim() } } 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 $host = $defaultHost $port = $defaultPort if ($envVars.ContainsKey("SCREENJOB_HOST") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_HOST"])) { $host = $envVars["SCREENJOB_HOST"] } if ($envVars.ContainsKey("SCREENJOB_PORT") -and -not [string]::IsNullOrWhiteSpace($envVars["SCREENJOB_PORT"])) { $port = $envVars["SCREENJOB_PORT"] } return "http://{0}:{1}/" -f $host, $port } function Update-TrayState { param( [System.Windows.Forms.NotifyIcon]$NotifyIcon, [System.Windows.Forms.ToolStripMenuItem]$StatusItem, [string]$Name ) $status = Get-ServiceStatusSafe -Name $Name $StatusItem.Text = "Status: $status" switch ($status) { "Running" { $NotifyIcon.Icon = [System.Drawing.SystemIcons]::Information } "Stopped" { $NotifyIcon.Icon = [System.Drawing.SystemIcons]::Warning } default { $NotifyIcon.Icon = [System.Drawing.SystemIcons]::Error } } $tooltip = "ScreenJob Backend: $status" 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)