Zum Inhalt

ApoAlly Insights Einrichtung#

Windows Server 2019#

Powershell Upload-Script
apoally-insights-upload.ps1
# upload_csv.ps1
#
# This script now automatically searches for an optional schema file (.json or .csvs)
# with the same base name as the CSV/TSV file. JSON has priority over CSVS if both exist.
# If no schema file is found, only the CSV/TSV will be uploaded.
#
# Environment:
#   $apiUrl      - Default: "https://api.apoally.de/insights/upload"
#   $apiKey      - Default: "YOUR-API-KEY"
#   $csvFolder   - Default: "D:\ApoAlly-Insights"
#   $waitMinutes - Default: 5
#
# This script checks each CSV/TSV file in the $csvFolder. If a file is older than $waitMinutes,
# it will be uploaded to the API. If an error occurs, an error.log is created.

# Configuration
$apiUrl = "https://api.apoally.de/insights/upload"
$apiKey = "YOUR-API-KEY"
$csvFolder = "D:\ApoAlly-Insights"
$waitMinutes = 5

function Upload-SingleFile {
    param (
        [string]$CsvFile
    )

    # Check for an existing JSON or CSVS schema file with the same base name (JSON has priority)
    $schemaFileJson = Join-Path $CsvFile.DirectoryName ("$($CsvFile.BaseName).json")
    $schemaFileCsvs = Join-Path $CsvFile.DirectoryName ("$($CsvFile.BaseName).csvs")
    $schemaFileToUpload = $null

    if (Test-Path $schemaFileJson) {
        $schemaFileToUpload = $schemaFileJson
    }
    elseif (Test-Path $schemaFileCsvs) {
        $schemaFileToUpload = $schemaFileCsvs
    }

    Write-Host "Uploading file: $($CsvFile.FullName)"
    if ($schemaFileToUpload) {
        Write-Host "Found optional schema file: $schemaFileToUpload"
    }

    # Determine MIME type based on extension (.csv or .tsv)
    $extension = $CsvFile.Extension.ToLower()
    $contentType = "text/csv"
    if ($extension -eq ".tsv") {
        $contentType = "text/tab-separated-values"
    }

    # Build the multipart/form-data body
    $boundary = "----MyBoundary$(Get-Random)"
    $LF = "`r`n"
    $bodyLines = @()

    # 1) Add the CSV/TSV part
    $csvContent = Get-Content $CsvFile.FullName -Raw
    $bodyLines += "--$boundary"
    $bodyLines += "Content-Disposition: form-data; name=`"files`"; filename=`"$($CsvFile.Name)`""
    $bodyLines += "Content-Type: $contentType$LF"
    $bodyLines += $csvContent

    # 2) If we have a schema file, add it as a second part
    if ($schemaFileToUpload) {
        $schemaContent = Get-Content $schemaFileToUpload -Raw
        $bodyLines += "--$boundary"
        $bodyLines += "Content-Disposition: form-data; name=`"files`"; filename=`"$($schemaFileToUpload)`""

        if ($schemaFileToUpload.ToLower().EndsWith(".json")) {
            $bodyLines += "Content-Type: application/json$LF"
        }
        else {
            # For .csvs, we can treat it as text
            $bodyLines += "Content-Type: text/plain$LF"
        }

        $bodyLines += $schemaContent
    }

    # End boundary
    $bodyLines += "--$boundary--"

    $body = $bodyLines -join $LF
    $headers = @{
        "X-API-Key" = $apiKey
        "Content-Type" = "multipart/form-data; boundary=$boundary"
    }

    try {
        $response = Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $body
        return $response
    }
    catch {
        throw $_
    }
}

function Upload-CSVFiles {
    param (
        [string]$Folder
    )

    # Get all CSV and TSV files recursively, ignoring those matching '_img.csv' or '_img.tsv'
    $csvFiles = Get-ChildItem -Path $Folder -Recurse -File -Include *.csv, *.tsv |
        Where-Object { $_.Name -notmatch '_img\.(csv|tsv)$' }

    foreach ($file in $csvFiles) {
        # Define the error log file name for the data file
        $errorLog = Join-Path $file.DirectoryName ("$($file.BaseName).error.log")

        # Skip if an error log already exists
        if (Test-Path $errorLog) {
            Write-Host "Skipping file due to existing error log: $($file.FullName)"
            continue
        }

        # Check if the file is at least $waitMinutes old
        $fileAge = (Get-Date) - $file.LastWriteTime
        if ($fileAge.TotalMinutes -lt $waitMinutes) {
            Write-Host "File is not old enough (Age: $([math]::Round($fileAge.TotalMinutes, 2)) minutes): $($file.FullName)"
            continue
        }

        Write-Host "Attempting upload of: $($file.FullName)"
        try {
            $response = Upload-SingleFile -CsvFile $file

            if ($response.status -eq "success") {
                Write-Host "Upload successful: $($file.FullName)"
                Remove-Item $file.FullName -Force

                # Check and delete any associated _img.csv or _img.tsv file
                $imgCsv = Join-Path $file.DirectoryName ("$($file.BaseName)_img.csv")
                $imgTsv = Join-Path $file.DirectoryName ("$($file.BaseName)_img.tsv")
                if (Test-Path $imgCsv) {
                    Remove-Item $imgCsv -Force
                    Write-Host "Deleted associated _img.csv file: $imgCsv"
                }
                if (Test-Path $imgTsv) {
                    Remove-Item $imgTsv -Force
                    Write-Host "Deleted associated _img.tsv file: $imgTsv"
                }
            }
            else {
                Write-Host "Upload failed for file: $($file.FullName). Creating error log."
                $errorMessage = "Error uploading: " + ($response.error)
                $errorMessage | Out-File -FilePath $errorLog -Encoding utf8
            }
        }
        catch {
            Write-Host "Error uploading file $($file.FullName): $_. Creating error log."
            $_ | Out-File -FilePath $errorLog -Encoding utf8
        }
    }
}

function Start-Watch {
    param (
        [string]$Folder,
        [int]$WaitMinutes
    )

    # Initial processing at startup
    Upload-CSVFiles -Folder $Folder

    # Endlessly repeat, waiting 1 minute between checks
    while ($true) {
        Start-Sleep -Seconds 60
        Upload-CSVFiles -Folder $Folder
    }
}

# Start folder watch
Start-Watch -Folder $csvFolder -WaitMinutes $waitMinutes

Anleitung zur Installation und Konfiguration des Scripts#

Die folgende Anleitung beschreibt, wie das PowerShell-Script apoally-insights-upload.ps1 auf einem Windows Server 2019 abgelegt, konfiguriert und so eingerichtet wird, dass es automatisch beim Systemstart ausgeführt wird.


1. Script speichern#

  1. Öffnen Sie den Editor (z. B. Notepad).
  2. Kopieren Sie den Inhalt des PowerShell-Scripts apoally-insights-upload.ps1 in den Editor.
  3. Speichern Sie die Datei unter dem Namen apoally-insights-upload.ps1 im gewünschten Verzeichnis, z. B. C:\Scripts.

2. PowerShell-Ausführungsrichtlinie anpassen#

Um das Script auszuführen, muss die PowerShell-Ausführungsrichtlinie angepasst werden:

  1. Öffnen Sie PowerShell als Administrator.
  2. Geben Sie den folgenden Befehl ein, um die Ausführungsrichtlinie zu ändern:
    Set-ExecutionPolicy RemoteSigned
    
  3. Bestätigen Sie die Änderung mit Y (Ja).

3. API-Key und Konfiguration anpassen#

  1. Öffnen Sie die Datei apoally-insights-upload.ps1 mit einem Texteditor.
  2. Ersetzen Sie YOUR-API-KEY durch Ihren tatsächlichen API-Schlüssel.
  3. Passen Sie den Pfad für den Ordner $csvFolder an, falls Ihre CSV-Dateien in einem anderen Verzeichnis gespeichert sind.
  4. Speichern Sie die Änderungen.

4. Task für Autostart einrichten#

Um das Script automatisch beim Systemstart auszuführen, richten Sie einen geplanten Task ein:

  1. Öffnen Sie den Task Scheduler (Aufgabenplanung) über das Startmenü.
  2. Klicken Sie auf Task erstellen.
  3. Geben Sie unter Allgemein einen Namen für den Task ein, z. B. ApoAlly Insights Upload.
  4. Aktivieren Sie die Option Mit höchsten Privilegien ausführen.
  5. Wechseln Sie zum Tab Trigger und klicken Sie auf Neu: - Wählen Sie Beim Start aus. - Klicken Sie auf OK.
  6. Wechseln Sie zum Tab Aktionen und klicken Sie auf Neu: - Wählen Sie Programm starten aus. - Geben Sie folgendes in das Feld Programm/Skript ein:
    powershell.exe
    
    - Geben Sie folgendes in das Feld Argumente hinzufügen (optional) ein:
    -File "C:\Scripts\apoally-insights-upload.ps1"
    
    - Klicken Sie auf OK.
  7. Wechseln Sie zum Tab Bedingungen und deaktivieren Sie die Option Task nur starten, wenn der Computer im Netzbetrieb ausgeführt wird, falls der Task auch im Akkubetrieb ausgeführt werden soll.
  8. Klicken Sie auf OK, um den Task zu erstellen.

5. Testlauf#

  1. Führen Sie das Script manuell aus, um sicherzustellen, dass es korrekt funktioniert:
    powershell.exe -File "C:\Scripts\apoally-insights-upload.ps1"
    
  2. Überprüfen Sie die Ausgabe und die erstellten Logdateien, um sicherzustellen, dass keine Fehler auftreten.

6. Überwachung und Fehlerbehebung#

  • Überprüfen Sie regelmäßig die Logdateien, die im gleichen Verzeichnis wie die CSV-Dateien gespeichert werden.
  • Falls der geplante Task nicht ausgeführt wird, überprüfen Sie die Einstellungen im Task Scheduler und stellen Sie sicher, dass das Script korrekt konfiguriert ist.

Mit diesen Schritten ist das Script erfolgreich auf Ihrem Windows Server 2019 eingerichtet und wird automatisch beim Start des Systems ausgeführt.