454 lines
13 KiB
PowerShell
454 lines
13 KiB
PowerShell
#Requires -Version 5.1
|
|
|
|
param(
|
|
[switch]$SkipSelfUpdateCheck
|
|
)
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
KANBAN-Rational-Druck mit Versionspruefung, Self-Update, Private-Config-Ladung, SQL-Abfrage und n8n-Webhook.
|
|
|
|
.DESCRIPTION
|
|
Ablauf:
|
|
1. Optional Self-Update aus dem Public-Repo per Versionsvergleich
|
|
2. AZ_GITHUB_TOKEN pruefen
|
|
3. Private Konfiguration aus dem Private-Repo laden
|
|
4. Genau eine Datei aus Input-Ordner lesen
|
|
5. Dateiname ohne Endung als Belegnummer verwenden
|
|
6. SQL-Abfrage ausfuehren
|
|
7. Ergebnis an n8n senden
|
|
8. Logging in Datei
|
|
|
|
.NOTES
|
|
Repo: AZ-PowerShell-Pub
|
|
#>
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$script:ScriptName = 'KANBAN-Rational-Druck'
|
|
$script:ScriptVersion = '1.0.0'
|
|
$script:PublicRawUrl = 'https://git.az-gruppe.com/AZ-Intec-GmbH/AZ-PowerShell-Pub/raw/branch/main/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1'
|
|
$script:PrivateRawUrl = 'https://git.az-gruppe.com/AZ-Intec-GmbH/AZ-PowerShell-Prv/raw/branch/main/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1'
|
|
$script:LogFilePath = $null
|
|
|
|
#region Hilfsfunktionen
|
|
|
|
function Write-ConsoleLog
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Message,
|
|
|
|
[ValidateSet('INFO', 'WARN', 'ERROR', 'SUCCESS')]
|
|
[string]$Level = 'INFO'
|
|
)
|
|
|
|
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
|
$line = "[$timestamp] [$Level] $Message"
|
|
Write-Host $line
|
|
}
|
|
|
|
function Write-Log
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Message,
|
|
|
|
[ValidateSet('INFO', 'WARN', 'ERROR', 'SUCCESS')]
|
|
[string]$Level = 'INFO'
|
|
)
|
|
|
|
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
|
$line = "[$timestamp] [$Level] $Message"
|
|
|
|
Write-Host $line
|
|
|
|
if ($script:LogFilePath)
|
|
{
|
|
Add-Content -Path $script:LogFilePath -Value $line -Encoding UTF8
|
|
}
|
|
}
|
|
|
|
function Test-AzGithubToken
|
|
{
|
|
if ([string]::IsNullOrWhiteSpace($env:AZ_GITHUB_TOKEN))
|
|
{
|
|
throw "ERROR: Umgebungsvariable AZ_GITHUB_TOKEN ist nicht gesetzt."
|
|
}
|
|
}
|
|
|
|
function Get-ScriptVersionFromContent
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Content
|
|
)
|
|
|
|
$match = [regex]::Match($Content, "\`$script:ScriptVersion\s*=\s*'([^']+)'")
|
|
if (-not $match.Success)
|
|
{
|
|
throw "ERROR: ScriptVersion konnte aus dem Remote-Skript nicht gelesen werden."
|
|
}
|
|
|
|
return $match.Groups[1].Value
|
|
}
|
|
|
|
function Start-SelfUpdateIfNeeded
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RemoteScriptUrl
|
|
)
|
|
|
|
if ($SkipSelfUpdateCheck)
|
|
{
|
|
Write-ConsoleLog "Self-Update-Pruefung wurde uebersprungen (Restart nach Update)." "INFO"
|
|
return
|
|
}
|
|
|
|
$localScriptPath = $PSCommandPath
|
|
if ([string]::IsNullOrWhiteSpace($localScriptPath))
|
|
{
|
|
throw "ERROR: Lokaler Skriptpfad konnte nicht ermittelt werden."
|
|
}
|
|
|
|
Write-ConsoleLog "Pruefe auf neue Public-Version aus Git..." "INFO"
|
|
|
|
try
|
|
{
|
|
$remoteResponse = Invoke-WebRequest -Uri $RemoteScriptUrl -UseBasicParsing
|
|
$remoteContent = $remoteResponse.Content
|
|
|
|
if ([string]::IsNullOrWhiteSpace($remoteContent))
|
|
{
|
|
throw "Remote-Skriptinhalt ist leer."
|
|
}
|
|
|
|
$remoteVersion = Get-ScriptVersionFromContent -Content $remoteContent
|
|
$localVersion = $script:ScriptVersion
|
|
|
|
Write-ConsoleLog "Lokale Version: $localVersion | Remote Version: $remoteVersion" "INFO"
|
|
|
|
if ($remoteVersion -eq $localVersion)
|
|
{
|
|
Write-ConsoleLog "OK: Public-Skript ist bereits aktuell." "SUCCESS"
|
|
return
|
|
}
|
|
|
|
Write-ConsoleLog "WARN: Neue Public-Version gefunden. Aktualisiere lokales Skript..." "WARN"
|
|
|
|
$tempUpdatedScript = Join-Path $env:TEMP ("{0}-{1}.ps1" -f $script:ScriptName, [guid]::NewGuid().ToString())
|
|
Set-Content -Path $tempUpdatedScript -Value $remoteContent -Encoding UTF8
|
|
|
|
Copy-Item -Path $tempUpdatedScript -Destination $localScriptPath -Force
|
|
Remove-Item -Path $tempUpdatedScript -Force -ErrorAction SilentlyContinue
|
|
|
|
Write-ConsoleLog "OK: Lokales Public-Skript wurde aktualisiert." "SUCCESS"
|
|
Write-ConsoleLog "Starte aktualisierte Version neu..." "INFO"
|
|
|
|
$argumentList = @(
|
|
'-ExecutionPolicy', 'Bypass',
|
|
'-File', $localScriptPath,
|
|
'-SkipSelfUpdateCheck'
|
|
)
|
|
|
|
Start-Process -FilePath 'powershell.exe' -ArgumentList $argumentList | Out-Null
|
|
|
|
Write-ConsoleLog "Aktuelle Instanz wird beendet, damit die neue Version ausgefuehrt wird." "INFO"
|
|
exit 0
|
|
} catch
|
|
{
|
|
throw "ERROR: Fehler bei der Self-Update-Pruefung des Public-Skripts: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
function Get-PrivateConfig
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$RawConfigUrl
|
|
)
|
|
|
|
$token = $env:AZ_GITHUB_TOKEN
|
|
$headers = @{
|
|
Authorization = "token $token"
|
|
}
|
|
|
|
Write-ConsoleLog "Lade private Konfiguration aus dem Private-Repo..." "INFO"
|
|
|
|
try
|
|
{
|
|
$response = Invoke-WebRequest -Uri $RawConfigUrl -Headers $headers -UseBasicParsing
|
|
$content = $response.Content
|
|
|
|
$tempFile = Join-Path $env:TEMP ("KANBAN-Rational-Druck-private-{0}.ps1" -f ([guid]::NewGuid().ToString()))
|
|
Set-Content -Path $tempFile -Value $content -Encoding UTF8
|
|
|
|
. $tempFile
|
|
|
|
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
|
|
|
|
if (-not $script:PrivateConfig)
|
|
{
|
|
throw "Private Konfiguration wurde geladen, aber `$script:PrivateConfig ist nicht gesetzt."
|
|
}
|
|
|
|
return $script:PrivateConfig
|
|
} catch
|
|
{
|
|
throw "ERROR: Fehler beim Laden der privaten Konfiguration: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
function Test-RequiredConfig
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Config
|
|
)
|
|
|
|
$requiredKeys = @(
|
|
'InputFolder',
|
|
'LogFolder',
|
|
'WebhookUrl',
|
|
'SqlServer',
|
|
'SqlDatabase',
|
|
'SqlUser',
|
|
'SqlPassword',
|
|
'SqlQuery',
|
|
'SqlParameterName'
|
|
)
|
|
|
|
foreach ($key in $requiredKeys)
|
|
{
|
|
if (-not $Config.ContainsKey($key) -or [string]::IsNullOrWhiteSpace([string]$Config[$key]))
|
|
{
|
|
throw "ERROR: Fehlende Konfiguration: '$key'"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-SingleInputFile
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$InputFolder
|
|
)
|
|
|
|
if (-not (Test-Path -Path $InputFolder))
|
|
{
|
|
throw "ERROR: Input-Ordner existiert nicht: $InputFolder"
|
|
}
|
|
|
|
$files = Get-ChildItem -Path $InputFolder -File
|
|
|
|
if ($files.Count -eq 0)
|
|
{
|
|
throw "ERROR: Keine Datei im Input-Ordner gefunden: $InputFolder"
|
|
}
|
|
|
|
if ($files.Count -gt 1)
|
|
{
|
|
throw "ERROR: Es wurden mehrere Dateien gefunden. Erwartet wird genau eine Datei im Ordner: $InputFolder"
|
|
}
|
|
|
|
return $files[0]
|
|
}
|
|
|
|
function Invoke-SqlTopOneQuery
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Config,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Belegnummer
|
|
)
|
|
|
|
$sqlPasswordPlain = [System.Text.Encoding]::UTF8.GetString(
|
|
[System.Convert]::FromBase64String($Config.SqlPassword)
|
|
)
|
|
|
|
Write-Log "Starte SQL-Abfrage mit Parameter $($Config.SqlParameterName) = '$Belegnummer'..." "INFO"
|
|
|
|
$connectionString = "Server={0};Database={1};User ID={2};Password={3};" -f `
|
|
$Config.SqlServer,
|
|
$Config.SqlDatabase,
|
|
$Config.SqlUser,
|
|
$sqlPasswordPlain
|
|
|
|
$connection = New-Object System.Data.SqlClient.SqlConnection
|
|
$connection.ConnectionString = $connectionString
|
|
|
|
try
|
|
{
|
|
$connection.Open()
|
|
|
|
$command = $connection.CreateCommand()
|
|
$command.CommandText = $Config.SqlQuery
|
|
$command.CommandTimeout = 60
|
|
|
|
[void]$command.Parameters.AddWithValue($Config.SqlParameterName, $Belegnummer)
|
|
|
|
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $command
|
|
$dataTable = New-Object System.Data.DataTable
|
|
[void]$adapter.Fill($dataTable)
|
|
|
|
if ($dataTable.Rows.Count -eq 0)
|
|
{
|
|
Write-Log "WARN: Kein SQL-Datensatz gefunden." "WARN"
|
|
return $null
|
|
}
|
|
|
|
$row = $dataTable.Rows[0]
|
|
$result = [ordered]@{}
|
|
|
|
foreach ($column in $dataTable.Columns)
|
|
{
|
|
$value = $row[$column.ColumnName]
|
|
if ($value -eq [System.DBNull]::Value)
|
|
{
|
|
$result[$column.ColumnName] = $null
|
|
} else
|
|
{
|
|
$result[$column.ColumnName] = $value
|
|
}
|
|
}
|
|
|
|
Write-Log "OK: SQL-Datensatz erfolgreich gelesen." "SUCCESS"
|
|
return [pscustomobject]$result
|
|
} catch
|
|
{
|
|
throw "ERROR: Fehler bei SQL-Abfrage: $($_.Exception.Message)"
|
|
} finally
|
|
{
|
|
if ($connection.State -ne [System.Data.ConnectionState]::Closed)
|
|
{
|
|
$connection.Close()
|
|
}
|
|
|
|
$connection.Dispose()
|
|
}
|
|
}
|
|
|
|
function Send-N8nWebhook
|
|
{
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WebhookUrl,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Payload
|
|
)
|
|
|
|
Write-Log "Sende Payload an n8n-Webhook..." "INFO"
|
|
|
|
try
|
|
{
|
|
$json = $Payload | ConvertTo-Json -Depth 10
|
|
$null = Invoke-RestMethod -Uri $WebhookUrl -Method Post -ContentType 'application/json' -Body $json
|
|
Write-Log "OK: Webhook erfolgreich gesendet." "SUCCESS"
|
|
} catch
|
|
{
|
|
throw "ERROR: Fehler beim Senden des Webhooks: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Hauptlogik
|
|
|
|
try
|
|
{
|
|
Start-SelfUpdateIfNeeded -RemoteScriptUrl $script:PublicRawUrl
|
|
|
|
Test-AzGithubToken
|
|
|
|
$config = Get-PrivateConfig -RawConfigUrl $script:PrivateRawUrl
|
|
Test-RequiredConfig -Config $config
|
|
|
|
if (-not (Test-Path -Path $config.LogFolder))
|
|
{
|
|
New-Item -Path $config.LogFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
$script:LogFilePath = Join-Path $config.LogFolder ("KANBAN-Rational-Druck_{0}.log" -f (Get-Date -Format 'yyyy-MM-dd'))
|
|
|
|
Write-Log "Starte Skriptausfuehrung KANBAN-Rational-Druck..." "INFO"
|
|
|
|
$inputFile = Get-SingleInputFile -InputFolder $config.InputFolder
|
|
|
|
$fileName = $inputFile.Name
|
|
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($inputFile.Name)
|
|
$fileFullPath = $inputFile.FullName
|
|
$fileExtension = $inputFile.Extension
|
|
|
|
Write-Log "Gefundene Datei: $fileName" "INFO"
|
|
Write-Log "Extrahierte Belegnummer: $fileBaseName" "INFO"
|
|
|
|
$sqlResult = $null
|
|
$sqlFound = $false
|
|
$status = 'success'
|
|
$message = 'SQL-Datensatz gefunden'
|
|
|
|
try
|
|
{
|
|
$sqlResult = Invoke-SqlTopOneQuery -Config $config -Belegnummer $fileBaseName
|
|
|
|
if ($null -eq $sqlResult)
|
|
{
|
|
$sqlFound = $false
|
|
$status = 'warning'
|
|
$message = 'Kein SQL-Datensatz gefunden'
|
|
} else
|
|
{
|
|
$sqlFound = $true
|
|
}
|
|
} catch
|
|
{
|
|
Write-Log $_.Exception.Message "ERROR"
|
|
$sqlFound = $false
|
|
$status = 'error'
|
|
$message = $_.Exception.Message
|
|
}
|
|
|
|
$payload = [ordered]@{
|
|
status = $status
|
|
message = $message
|
|
fileName = $fileName
|
|
fileBaseName = $fileBaseName
|
|
fileFullPath = $fileFullPath
|
|
fileExtension = $fileExtension
|
|
processedAt = (Get-Date).ToString('s')
|
|
sqlFound = $sqlFound
|
|
sqlResult = $sqlResult
|
|
}
|
|
|
|
Send-N8nWebhook -WebhookUrl $config.WebhookUrl -Payload $payload
|
|
|
|
Write-Log "Skriptausfuehrung abgeschlossen." "SUCCESS"
|
|
} catch
|
|
{
|
|
try
|
|
{
|
|
if (-not $script:LogFilePath)
|
|
{
|
|
$fallbackFolder = Join-Path $env:TEMP 'KANBAN-Rational-Druck'
|
|
if (-not (Test-Path $fallbackFolder))
|
|
{
|
|
New-Item -Path $fallbackFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
$script:LogFilePath = Join-Path $fallbackFolder ("KANBAN-Rational-Druck_{0}.log" -f (Get-Date -Format 'yyyy-MM-dd'))
|
|
}
|
|
|
|
Write-Log $_.Exception.Message "ERROR"
|
|
} catch
|
|
{
|
|
Write-Host "ERROR: Kritischer Fehler: $($_.Exception.Message)"
|
|
}
|
|
|
|
exit 1
|
|
}
|
|
|
|
#endregion
|