Revert "merge: apply windows standalone service installer and awHostname hardening"

This reverts commit e643576aa9, reversing
changes made to 669501f20a.
This commit is contained in:
igor04091968
2026-05-08 00:41:00 +03:00
parent e643576aa9
commit 6f5e5eb751
71 changed files with 23918 additions and 19579 deletions
@@ -306,9 +306,6 @@ function Copy-ActivityWatchCollectorAssets {
$resolvedRules = Resolve-Path -LiteralPath $CustomRulesSource -ErrorAction Stop
Copy-Item -LiteralPath $resolvedRules.Path -Destination $rulesTarget -Force
}
else {
Copy-Item -LiteralPath $exampleRulesTarget -Destination $rulesTarget -Force
}
if ($CustomPolicySource) {
$resolvedPolicy = Resolve-Path -LiteralPath $CustomPolicySource -ErrorAction Stop
@@ -537,7 +534,11 @@ function Get-CollectorPowerShellProcessCount {
function New-LaunchLock {
param([string]`$StateRoot, [int]`$SessionId)
`$lockPath = Join-Path `$env:TEMP ("launch-watchers-session-{0}.lock" -f `$SessionId)
if (-not (Test-Path -LiteralPath `$StateRoot)) {
New-Item -Path `$StateRoot -ItemType Directory -Force | Out-Null
}
`$lockPath = Join-Path `$StateRoot ("launch-watchers-session-{0}.lock" -f `$SessionId)
if (Test-Path -LiteralPath `$lockPath) {
try {
`$lockData = Get-Content -LiteralPath `$lockPath -Raw | ConvertFrom-Json
@@ -730,11 +731,13 @@ function Start-CollectorScriptIfNeeded {
return
}
`$staParam = if (`$ScriptPath -like "*endpoint-signals*") { "-STA" } else { `$null }
`$argumentList = @('-NoProfile', '-WindowStyle', 'Hidden', '-ExecutionPolicy', 'Bypass')
if (`$staParam) { `$argumentList += `$staParam }
`$argumentList += @('-File', `$ScriptPath, '-ConfigPath', `$ConfigPath)
Start-Process -FilePath `$PowerShellExe -ArgumentList `$argumentList -WindowStyle Hidden
Start-Process -FilePath `$PowerShellExe -ArgumentList @(
'-NoProfile',
'-WindowStyle', 'Hidden',
'-ExecutionPolicy', 'Bypass',
'-File', `$ScriptPath,
'-ConfigPath', `$ConfigPath
) -WindowStyle Hidden
}
`$config = Get-DeploymentConfig -Path `$ConfigPath
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
[CmdletBinding()]
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ServerHost,
@@ -112,8 +112,8 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Output 'ActivityWatch развёрнут для пользователей:'
$targetUsers | ForEach-Object { Write-Output " - $_" }
Write-Output "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Output "Каталог данных: $StateRoot"
Write-Output "Файл DLP-политики: $($assetResult.ActivePolicy)"
Write-Host 'ActivityWatch развёрнут для пользователей:'
$targetUsers | ForEach-Object { Write-Host " - $_" }
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
@@ -1,4 +1,4 @@
[CmdletBinding()]
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ServerHost,
@@ -136,6 +136,6 @@ if ($reportDirectory) {
$report | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $effectiveReportPath -Encoding UTF8
Write-Output 'Комплексное развёртывание ActivityWatch завершено.'
Write-Output "Пользователи: $($resolvedUsers -join ', ')"
Write-Output "Отчёт: $effectiveReportPath"
Write-Host 'Комплексное развёртывание ActivityWatch завершено.'
Write-Host "Пользователи: $($resolvedUsers -join ', ')"
Write-Host "Отчёт: $effectiveReportPath"
@@ -1,4 +1,4 @@
[CmdletBinding()]
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ServerHost,
@@ -104,9 +104,9 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $recoveryScriptPath -ConfigPath $configPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Output "ActivityWatch развёрнут для пользователя: $TargetUser"
Write-Output "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Output "Каталог установки: $InstallRoot"
Write-Output "Каталог данных: $StateRoot"
Write-Output "Файл правил: $($assetResult.ActiveRules)"
Write-Output "Файл DLP-политики: $($assetResult.ActivePolicy)"
Write-Host "ActivityWatch развёрнут для пользователя: $TargetUser"
Write-Host "Сервер: ${ServerScheme}://$ServerHost`:$ServerPort"
Write-Host "Каталог установки: $InstallRoot"
Write-Host "Каталог данных: $StateRoot"
Write-Host "Файл правил: $($assetResult.ActiveRules)"
Write-Host "Файл DLP-политики: $($assetResult.ActivePolicy)"
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
[CmdletBinding()]
[CmdletBinding()]
param(
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
[string]$ServerHost,
@@ -151,6 +151,6 @@ Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptP
Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $effectiveRecoveryScript -ConfigPath $effectiveConfigPath
Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
Write-Output 'Укрепление и восстановление ActivityWatch завершены.'
Write-Output "Конфигурация: $effectiveConfigPath"
Write-Output "Пользователи восстановлены: $($effectiveUsers -join ', ')"
Write-Host 'Укрепление и восстановление ActivityWatch завершены.'
Write-Host "Конфигурация: $effectiveConfigPath"
Write-Host "Пользователи восстановлены: $($effectiveUsers -join ', ')"
@@ -1,4 +1,4 @@
[CmdletBinding(SupportsShouldProcess = $true)]
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string]$OldInstallRoot = 'C:\Program Files\ActivityWatch-Phase2',
[string]$OldStateRoot = 'C:\ProgramData\ActivityWatch-Phase2',
@@ -154,36 +154,7 @@ if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Миграция ActivityWatch W
@{ Source = $NewStateRoot; Name = 'new-state' }
)) {
if (Test-Path -LiteralPath $item.Source) {
$backupDest = Join-Path $backupRoot $item.Name
New-ActivityWatchDirectory -Path $backupDest
$excludeDirs = @()
if ($item.Source -eq $NewStateRoot) {
# Avoid infinite recursion: backupRoot is inside NewStateRoot by default.
$excludeDirs += $backupRoot
}
$robocopyArgs = @(
$item.Source,
$backupDest,
'/E',
'/R:1',
'/W:1',
'/NFL',
'/NDL',
'/NJH',
'/NJS',
'/NP'
)
if ($excludeDirs.Count -gt 0) {
$robocopyArgs += '/XD'
$robocopyArgs += $excludeDirs
}
& robocopy @robocopyArgs | Out-Null
if ($LASTEXITCODE -ge 8) {
throw "Backup robocopy failed (exit=$LASTEXITCODE) for source '$($item.Source)' to '$backupDest'"
}
Copy-Item -LiteralPath $item.Source -Destination (Join-Path $backupRoot $item.Name) -Recurse -Force
}
}
@@ -1,6 +1,6 @@
[CmdletBinding()]
param(
[string]$ConfigPath = 'C:\ProgramData\ActivityWatch\deployment-config.json'
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json'
)
Set-StrictMode -Version Latest
@@ -13,26 +13,49 @@ $config = Read-ActivityWatchDeploymentConfig -Path $ConfigPath
$installRoot = [string]$config.paths.installRoot
$stateRoot = [string]$config.paths.stateRoot
$collectorScript = [string]$config.paths.collectorScript
$endpointCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'endpointCollectorScript') { [string]$config.paths.endpointCollectorScript } else { Join-Path $stateRoot 'dlp-endpoint-signals-collector.ps1' }
$sessionCollectorScript = if ($config.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') { [string]$config.paths.sessionCollectorScript } else { Join-Path $stateRoot 'worktime-session-collector.ps1' }
$rulesPath = [string]$config.paths.rulesPath
$policyPath = if ($config.paths.PSObject.Properties.Name -contains 'policyPath') { [string]$config.paths.policyPath } else { Join-Path $stateRoot 'dlp-policy.json' }
$launchScript = [string]$config.paths.launchScript
$recoveryScript = [string]$config.paths.recoveryScript
$afkExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'afkEnabled') { [bool]$config.collectors.afkEnabled } else { $true }
$windowExpected = if ($config.PSObject.Properties.Name -contains 'collectors' -and $config.collectors.PSObject.Properties.Name -contains 'windowEnabled') { [bool]$config.collectors.windowEnabled } else { $true }
$requiredFiles = @(
(Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe'),
(Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe'),
$collectorScript,
$endpointCollectorScript,
$sessionCollectorScript,
$rulesPath,
$policyPath,
$launchScript,
$recoveryScript,
$ConfigPath
)
if ($afkExpected) {
$requiredFiles += (Join-Path $installRoot 'aw-watcher-afk\aw-watcher-afk.exe')
}
if ($windowExpected) {
$requiredFiles += (Join-Path $installRoot 'aw-watcher-window\aw-watcher-window.exe')
}
$missingFiles = @(
$requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_) }
)
$processNames = @('aw-watcher-afk', 'aw-watcher-window')
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
$processNames = @()
if ($afkExpected) { $processNames += 'aw-watcher-afk' }
if ($windowExpected) { $processNames += 'aw-watcher-window' }
$runningProcesses = @()
if ($processNames.Count -gt 0) {
$runningProcesses = Get-Process -Name $processNames -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId
}
$sessionCollectorProcesses = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object {
($_.Name -ieq 'powershell.exe' -or $_.Name -ieq 'pwsh.exe') -and
$_.CommandLine -match [Regex]::Escape($sessionCollectorScript)
} |
Select-Object Name, ProcessId, SessionId, CommandLine
$taskNames = @()
if ($config.userTasks) {
@@ -42,7 +65,7 @@ $taskNames += [string]$config.recovery.taskName
$taskNames = $taskNames | Sort-Object -Unique
$tasks = foreach ($taskName in $taskNames) {
$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
$task = Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -eq $taskName } | Select-Object -First 1
if ($task) {
[pscustomobject]@{
taskName = $task.TaskName
@@ -53,7 +76,7 @@ $tasks = foreach ($taskName in $taskNames) {
else {
[pscustomobject]@{
taskName = $taskName
state = 'Missing'
state = 'Отсутствует'
present = $false
}
}
@@ -76,8 +99,16 @@ $result = [ordered]@{
ok = [bool]($tasks.Count -gt 0 -and -not ($tasks | Where-Object { -not $_.present }))
}
processes = [ordered]@{
expected = $processNames
list = @($runningProcesses)
ok = [bool](($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge 2)
sessionCollectors = @($sessionCollectorProcesses)
ok = [bool](
(
($processNames.Count -eq 0) -or
(($runningProcesses | Select-Object -ExpandProperty Name -Unique).Count -ge $processNames.Count)
) -and
($sessionCollectorProcesses.Count -ge 1)
)
}
}
@@ -1,44 +1,4 @@
param(
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
[string]$Hostname,
[int]$PollSeconds = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-Config {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Конфигурация не найдена: $Path"
}
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}
function Invoke-AwJsonPost {
param(
[Parameter(Mandatory = $true)][string]$Uri,
[Parameter(Mandatory = $true)][string]$Json
)
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
}
function Ensure-Bucket {
param(
[Parameter(Mandatory = $true)][string]$ApiBase,
[Parameter(Mandatory = $true)][string]$BucketId,
[Parameter(Mandatory = $true)][string]$HostnameValue
)
try {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
return
}
catch { Write-Error param(
param(
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
[string]$Hostname,
[int]$PollSeconds = 30
@@ -87,12 +47,7 @@ function Ensure-Bucket {
hostname = $HostnameValue
} | ConvertTo-Json -Compress
try {
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
catch {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
}
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
function Get-SessionRecords {
@@ -204,458 +159,3 @@ while ($true) {
Start-Sleep -Seconds $sleepSec
}
; }
$body = @{
client = 'aw-worktime-session-collector'
type = 'aw.worktime.session'
hostname = $HostnameValue
} | ConvertTo-Json -Compress
try {
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
catch {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
}
}
function Get-SessionRecords {
$records = @()
try {
$lines = quser 2>$null
if (-not $lines) {
return @()
}
foreach ($line in ($lines | Select-Object -Skip 1)) {
$clean = ($line -replace '^\s*>?', '').Trim()
if (-not $clean) {
continue
}
$parts = $clean -split '\s+'
if ($parts.Count -lt 4) {
continue
}
$sessionName = ''
$sessionIdIndex = 2
if ($parts[1] -match '^\d+$') {
$sessionIdIndex = 1
}
else {
$sessionName = $parts[1]
}
$sessionId = 0
if ($parts[$sessionIdIndex] -match '^\d+$') {
$sessionId = [int]$parts[$sessionIdIndex]
}
$records += [pscustomobject]@{
username = $parts[0]
sessionName = $sessionName
sessionId = $sessionId
state = $parts[$sessionIdIndex + 1]
}
}
}
catch { Write-Error param(
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
[string]$Hostname,
[int]$PollSeconds = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-Config {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Конфигурация не найдена: $Path"
}
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}
function Invoke-AwJsonPost {
param(
[Parameter(Mandatory = $true)][string]$Uri,
[Parameter(Mandatory = $true)][string]$Json
)
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
}
function Ensure-Bucket {
param(
[Parameter(Mandatory = $true)][string]$ApiBase,
[Parameter(Mandatory = $true)][string]$BucketId,
[Parameter(Mandatory = $true)][string]$HostnameValue
)
try {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
return
}
catch {
}
$body = @{
client = 'aw-worktime-session-collector'
type = 'aw.worktime.session'
hostname = $HostnameValue
} | ConvertTo-Json -Compress
try {
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
catch {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
}
}
function Get-SessionRecords {
$records = @()
try {
$lines = quser 2>$null
if (-not $lines) {
return @()
}
foreach ($line in ($lines | Select-Object -Skip 1)) {
$clean = ($line -replace '^\s*>?', '').Trim()
if (-not $clean) {
continue
}
$parts = $clean -split '\s+'
if ($parts.Count -lt 4) {
continue
}
$sessionName = ''
$sessionIdIndex = 2
if ($parts[1] -match '^\d+$') {
$sessionIdIndex = 1
}
else {
$sessionName = $parts[1]
}
$sessionId = 0
if ($parts[$sessionIdIndex] -match '^\d+$') {
$sessionId = [int]$parts[$sessionIdIndex]
}
$records += [pscustomobject]@{
username = $parts[0]
sessionName = $sessionName
sessionId = $sessionId
state = $parts[$sessionIdIndex + 1]
}
}
}
catch {
}
return $records
}
function Test-SessionIsActive {
param([AllowNull()][string]$State)
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
$s = $State.Trim().ToLowerInvariant()
return ($s -eq 'active') -or ($s -like 'актив*')
}
$cfg = Get-Config -Path $ConfigPath
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
$bucketId = 'aw-worktime-sessions_' + $hostValue
$pulse = 120
$sleepSec = if ($PollSeconds -gt 0) {
$PollSeconds
}
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
[int]$cfg.collector.pollSeconds
}
else {
30
}
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
while ($true) {
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$records = Get-SessionRecords
if (-not $records -or $records.Count -eq 0) {
$records = @([pscustomobject]@{
username = $env:USERNAME
sessionName = ''
sessionId = (Get-Process -Id $PID).SessionId
state = 'Unknown'
})
}
foreach ($rec in $records) {
$payload = @{
timestamp = $now
duration = 0
data = @{
username = [string]$rec.username
userId = "$($env:USERDOMAIN)\$($rec.username)"
sessionId = [int]$rec.sessionId
sessionName = [string]$rec.sessionName
state = [string]$rec.state
active = (Test-SessionIsActive -State ([string]$rec.state))
hostname = $hostValue
source = 'worktime-session-collector'
}
} | ConvertTo-Json -Depth 6 -Compress
try {
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
}
catch {
}
}
Start-Sleep -Seconds $sleepSec
}
; }
return $records
}
function Test-SessionIsActive {
param([AllowNull()][string]$State)
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
$s = $State.Trim().ToLowerInvariant()
return ($s -eq 'active') -or ($s -like 'актив*')
}
$cfg = Get-Config -Path $ConfigPath
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
$bucketId = 'aw-worktime-sessions_' + $hostValue
$pulse = 120
$sleepSec = if ($PollSeconds -gt 0) {
$PollSeconds
}
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
[int]$cfg.collector.pollSeconds
}
else {
30
}
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
while ($true) {
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$records = Get-SessionRecords
if (-not $records -or $records.Count -eq 0) {
$records = @([pscustomobject]@{
username = $env:USERNAME
sessionName = ''
sessionId = (Get-Process -Id $PID).SessionId
state = 'Unknown'
})
}
foreach ($rec in $records) {
$payload = @{
timestamp = $now
duration = 0
data = @{
username = [string]$rec.username
userId = "$($env:USERDOMAIN)\$($rec.username)"
sessionId = [int]$rec.sessionId
sessionName = [string]$rec.sessionName
state = [string]$rec.state
active = (Test-SessionIsActive -State ([string]$rec.state))
hostname = $hostValue
source = 'worktime-session-collector'
}
} | ConvertTo-Json -Depth 6 -Compress
try {
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
}
catch { Write-Error param(
[string]$ConfigPath = 'C:\ProgramData\AWatch-rus\deployment-config.json',
[string]$Hostname,
[int]$PollSeconds = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-Config {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Конфигурация не найдена: $Path"
}
Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}
function Invoke-AwJsonPost {
param(
[Parameter(Mandatory = $true)][string]$Uri,
[Parameter(Mandatory = $true)][string]$Json
)
$bytes = [Text.Encoding]::UTF8.GetBytes($Json)
Invoke-RestMethod -Method Post -Uri $Uri -ContentType 'application/json; charset=utf-8' -Body $bytes | Out-Null
}
function Ensure-Bucket {
param(
[Parameter(Mandatory = $true)][string]$ApiBase,
[Parameter(Mandatory = $true)][string]$BucketId,
[Parameter(Mandatory = $true)][string]$HostnameValue
)
try {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
return
}
catch {
}
$body = @{
client = 'aw-worktime-session-collector'
type = 'aw.worktime.session'
hostname = $HostnameValue
} | ConvertTo-Json -Compress
try {
Invoke-AwJsonPost -Uri "$ApiBase/buckets/$BucketId" -Json $body
}
catch {
Invoke-RestMethod -Method Get -Uri "$ApiBase/buckets/$BucketId" | Out-Null
}
}
function Get-SessionRecords {
$records = @()
try {
$lines = quser 2>$null
if (-not $lines) {
return @()
}
foreach ($line in ($lines | Select-Object -Skip 1)) {
$clean = ($line -replace '^\s*>?', '').Trim()
if (-not $clean) {
continue
}
$parts = $clean -split '\s+'
if ($parts.Count -lt 4) {
continue
}
$sessionName = ''
$sessionIdIndex = 2
if ($parts[1] -match '^\d+$') {
$sessionIdIndex = 1
}
else {
$sessionName = $parts[1]
}
$sessionId = 0
if ($parts[$sessionIdIndex] -match '^\d+$') {
$sessionId = [int]$parts[$sessionIdIndex]
}
$records += [pscustomobject]@{
username = $parts[0]
sessionName = $sessionName
sessionId = $sessionId
state = $parts[$sessionIdIndex + 1]
}
}
}
catch {
}
return $records
}
function Test-SessionIsActive {
param([AllowNull()][string]$State)
if ([string]::IsNullOrWhiteSpace($State)) { return $false }
$s = $State.Trim().ToLowerInvariant()
return ($s -eq 'active') -or ($s -like 'актив*')
}
$cfg = Get-Config -Path $ConfigPath
$hostValue = if ($Hostname) { $Hostname } else { [string]$env:COMPUTERNAME }
$apiBase = '{0}://{1}:{2}/api/0' -f [string]$cfg.server.scheme, [string]$cfg.server.host, [string]$cfg.server.port
$bucketId = 'aw-worktime-sessions_' + $hostValue
$pulse = 120
$sleepSec = if ($PollSeconds -gt 0) {
$PollSeconds
}
elseif ($cfg.collector -and $cfg.collector.pollSeconds) {
[int]$cfg.collector.pollSeconds
}
else {
30
}
Ensure-Bucket -ApiBase $apiBase -BucketId $bucketId -HostnameValue $hostValue
while ($true) {
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$records = Get-SessionRecords
if (-not $records -or $records.Count -eq 0) {
$records = @([pscustomobject]@{
username = $env:USERNAME
sessionName = ''
sessionId = (Get-Process -Id $PID).SessionId
state = 'Unknown'
})
}
foreach ($rec in $records) {
$payload = @{
timestamp = $now
duration = 0
data = @{
username = [string]$rec.username
userId = "$($env:USERDOMAIN)\$($rec.username)"
sessionId = [int]$rec.sessionId
sessionName = [string]$rec.sessionName
state = [string]$rec.state
active = (Test-SessionIsActive -State ([string]$rec.state))
hostname = $hostValue
source = 'worktime-session-collector'
}
} | ConvertTo-Json -Depth 6 -Compress
try {
Invoke-AwJsonPost -Uri "$apiBase/buckets/$bucketId/heartbeat?pulsetime=$pulse" -Json $payload
}
catch {
}
}
Start-Sleep -Seconds $sleepSec
}
; }
}
Start-Sleep -Seconds $sleepSec
}