# Sync-AD2-NAS.ps1 (formerly Sync-FromNAS.ps1) # Bidirectional sync between AD2 and NAS (D2TESTNAS) # # PULL (NAS → AD2): Test results (LOGS/*.DAT, Reports/*.TXT) → Database import # PUSH (AD2 → NAS): Software updates (ProdSW/*, TODO.BAT) → DOS machines # # Run: powershell -ExecutionPolicy Bypass -File C:\Shares\test\scripts\Sync-FromNAS.ps1 # Scheduled: Every 15 minutes via Windows Task Scheduler param( [switch]$DryRun, # Show what would be done without doing it [switch]$Verbose, # Extra output [int]$MaxAgeMinutes = 1440 # Default: files from last 24 hours (was 60 min, too aggressive) ) # ============================================================================ # Configuration # ============================================================================ $NAS_IP = "192.168.0.9" $NAS_USER = "root" $NAS_PASSWORD = "Paper123!@#-nas" $NAS_HOSTKEY = "SHA256:5CVIPlqjLPxO8n48PKLAP99nE6XkEBAjTkaYmJAeOdA" $NAS_DATA_PATH = "/data/test" $AD2_TEST_PATH = "C:\Shares\test" $AD2_HISTLOGS_PATH = "C:\Shares\test\Ate\HISTLOGS" $SSH = "C:\Program Files\OpenSSH\ssh.exe" # Changed from PLINK to OpenSSH $SCP = "C:\Program Files\OpenSSH\scp.exe" # Changed from PSCP to OpenSSH $LOG_FILE = "C:\Shares\test\scripts\sync-from-nas.log" $STATUS_FILE = "C:\Shares\test\_SYNC_STATUS.txt" $LOG_TYPES = @("5BLOG", "7BLOG", "8BLOG", "DSCLOG", "SCTLOG", "VASLOG", "PWRLOG", "HVLOG") # Database import configuration $IMPORT_SCRIPT = "C:\Shares\testdatadb\database\import.js" $NODE_PATH = "node" # ============================================================================ # Functions # ============================================================================ function Write-Log { param([string]$Message) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logLine = "$timestamp : $Message" Add-Content -Path $LOG_FILE -Value $logLine if ($Verbose) { Write-Host $logLine } } function Invoke-NASCommand { param([string]$Command) $result = & $SSH -i "C:\Users\sysadmin\.ssh\id_ed25519" -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new $Command 2>&1 return $result } function Copy-FromNAS { param( [string]$RemotePath, [string]$LocalPath ) # Ensure local directory exists $localDir = Split-Path -Parent $LocalPath if (-not (Test-Path $localDir)) { New-Item -ItemType Directory -Path $localDir -Force | Out-Null } $result = & $SCP -O -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile="C:\Shares\test\scripts\.ssh\known_hosts" "${NAS_USER}@${NAS_IP}:$RemotePath" $LocalPath 2>&1 if ($LASTEXITCODE -ne 0) { $errorMsg = $result | Out-String Write-Log " SCP PUSH ERROR (exit $LASTEXITCODE): $errorMsg" } return $LASTEXITCODE -eq 0 } function Remove-FromNAS { param([string]$RemotePath) Invoke-NASCommand "rm -f '$RemotePath'" | Out-Null } function Copy-ToNAS { param( [string]$LocalPath, [string]$RemotePath ) # Ensure remote directory exists $remoteDir = Split-Path -Parent $RemotePath Invoke-NASCommand "mkdir -p '$remoteDir'" | Out-Null $result = & $SCP -O -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile="C:\Shares\test\scripts\.ssh\known_hosts" $LocalPath "${NAS_USER}@${NAS_IP}:$RemotePath" 2>&1 if ($LASTEXITCODE -ne 0) { $errorMsg = $result | Out-String Write-Log " SCP PUSH ERROR (exit $LASTEXITCODE): $errorMsg" } return $LASTEXITCODE -eq 0 } function Get-FileHash256 { param([string]$FilePath) if (Test-Path $FilePath) { return (Get-FileHash -Path $FilePath -Algorithm SHA256).Hash } return $null } function Import-ToDatabase { param([string[]]$FilePaths) if ($FilePaths.Count -eq 0) { return } Write-Log "Importing $($FilePaths.Count) file(s) to database..." # Build argument list $args = @("$IMPORT_SCRIPT", "--file") + $FilePaths try { $output = & $NODE_PATH $args 2>&1 foreach ($line in $output) { Write-Log " [DB] $line" } Write-Log "Database import complete" } catch { Write-Log "ERROR: Database import failed: $_" } } # ============================================================================ # Main Script # ============================================================================ Write-Log "==========================================" Write-Log "Starting sync from NAS" Write-Log "Max age: $MaxAgeMinutes minutes" if ($DryRun) { Write-Log "DRY RUN - no changes will be made" } $errorCount = 0 $syncedFiles = 0 $skippedFiles = 0 $syncedDatFiles = @() # Track DAT files for database import # Find all DAT files on NAS modified within the time window Write-Log "Finding DAT files on NAS..." $findCommand = "find $NAS_DATA_PATH/TS-*/LOGS -name '*.DAT' -type f -mmin -$MaxAgeMinutes 2>/dev/null" $datFiles = Invoke-NASCommand $findCommand if (-not $datFiles -or $datFiles.Count -eq 0) { Write-Log "No new DAT files found on NAS" } else { Write-Log "Found $($datFiles.Count) DAT file(s) to process" foreach ($remoteFile in $datFiles) { $remoteFile = $remoteFile.Trim() if ([string]::IsNullOrWhiteSpace($remoteFile)) { continue } # Parse the path: /data/test/TS-XX/LOGS/7BLOG/file.DAT if ($remoteFile -match "/data/test/(TS-[^/]+)/LOGS/([^/]+)/(.+\.DAT)$") { $station = $Matches[1] $logType = $Matches[2] $fileName = $Matches[3] Write-Log "Processing: $station/$logType/$fileName" # Destination 1: Per-station folder (preserves structure) $stationDest = Join-Path $AD2_TEST_PATH "$station\LOGS\$logType\$fileName" # Destination 2: Aggregated HISTLOGS folder $histlogsDest = Join-Path $AD2_HISTLOGS_PATH "$logType\$fileName" if ($DryRun) { Write-Log " [DRY RUN] Would copy to: $stationDest" $syncedFiles++ } else { # Copy to station folder only (skip HISTLOGS to avoid duplicates) $success1 = Copy-FromNAS -RemotePath $remoteFile -LocalPath $stationDest if ($success1) { Write-Log " Copied to station folder" # Remove from NAS after successful sync Remove-FromNAS -RemotePath $remoteFile Write-Log " Removed from NAS" # Track for database import $syncedDatFiles += $stationDest $syncedFiles++ } else { Write-Log " ERROR: Failed to copy from NAS" $errorCount++ } } } else { Write-Log " Skipping (unexpected path format): $remoteFile" $skippedFiles++ } } } # Find and sync TXT report files Write-Log "Finding TXT reports on NAS..." $findReportsCommand = "find $NAS_DATA_PATH/TS-*/Reports -name '*.TXT' -type f -mmin -$MaxAgeMinutes 2>/dev/null" $txtFiles = Invoke-NASCommand $findReportsCommand if ($txtFiles -and $txtFiles.Count -gt 0) { Write-Log "Found $($txtFiles.Count) TXT report(s) to process" foreach ($remoteFile in $txtFiles) { $remoteFile = $remoteFile.Trim() if ([string]::IsNullOrWhiteSpace($remoteFile)) { continue } if ($remoteFile -match "/data/test/(TS-[^/]+)/Reports/(.+\.TXT)$") { $station = $Matches[1] $fileName = $Matches[2] Write-Log "Processing report: $station/$fileName" # Destination: Per-station Reports folder $reportDest = Join-Path $AD2_TEST_PATH "$station\Reports\$fileName" if ($DryRun) { Write-Log " [DRY RUN] Would copy to: $reportDest" $syncedFiles++ } else { $success = Copy-FromNAS -RemotePath $remoteFile -LocalPath $reportDest if ($success) { Write-Log " Copied report" Remove-FromNAS -RemotePath $remoteFile Write-Log " Removed from NAS" $syncedFiles++ } else { Write-Log " ERROR: Failed to copy report" $errorCount++ } } } } } # ============================================================================ # Import synced DAT files to database # ============================================================================ if (-not $DryRun -and $syncedDatFiles.Count -gt 0) { Import-ToDatabase -FilePaths $syncedDatFiles } # ============================================================================ # PUSH: AD2 → NAS (Software Updates for DOS Machines) # ============================================================================ Write-Log "--- AD2 to NAS Sync (Software Updates) ---" $pushedFiles = 0 # Sync COMMON/ProdSW (batch files for all stations) # AD2 uses _COMMON, NAS uses COMMON - handle both $commonSources = @( @{ Local = "$AD2_TEST_PATH\_COMMON\ProdSW"; Remote = "$NAS_DATA_PATH/COMMON/ProdSW" }, @{ Local = "$AD2_TEST_PATH\COMMON\ProdSW"; Remote = "$NAS_DATA_PATH/COMMON/ProdSW" } ) foreach ($source in $commonSources) { if (Test-Path $source.Local) { Write-Log "Syncing COMMON ProdSW from: $($source.Local)" $commonFiles = Get-ChildItem -Path $source.Local -File -ErrorAction SilentlyContinue foreach ($file in $commonFiles) { $remotePath = "$($source.Remote)/$($file.Name)" if ($DryRun) { Write-Log " [DRY RUN] Would push: $($file.Name) -> $remotePath" $pushedFiles++ } else { $success = Copy-ToNAS -LocalPath $file.FullName -RemotePath $remotePath if ($success) { Write-Log " Pushed: $($file.Name)" $pushedFiles++ } else { Write-Log " ERROR: Failed to push $($file.Name)" $errorCount++ } } } } } # Sync UPDATE.BAT (root level utility) Write-Log "Syncing UPDATE.BAT..." $updateBatLocal = "$AD2_TEST_PATH\UPDATE.BAT" if (Test-Path $updateBatLocal) { $updateBatRemote = "$NAS_DATA_PATH/UPDATE.BAT" if ($DryRun) { Write-Log " [DRY RUN] Would push: UPDATE.BAT -> $updateBatRemote" $pushedFiles++ } else { $success = Copy-ToNAS -LocalPath $updateBatLocal -RemotePath $updateBatRemote if ($success) { Write-Log " Pushed: UPDATE.BAT" $pushedFiles++ } else { Write-Log " ERROR: Failed to push UPDATE.BAT" $errorCount++ } } } else { Write-Log " WARNING: UPDATE.BAT not found at $updateBatLocal" } # Sync DEPLOY.BAT (root level utility) Write-Log "Syncing DEPLOY.BAT..." $deployBatLocal = "$AD2_TEST_PATH\DEPLOY.BAT" if (Test-Path $deployBatLocal) { $deployBatRemote = "$NAS_DATA_PATH/DEPLOY.BAT" if ($DryRun) { Write-Log " [DRY RUN] Would push: DEPLOY.BAT -> $deployBatRemote" $pushedFiles++ } else { $success = Copy-ToNAS -LocalPath $deployBatLocal -RemotePath $deployBatRemote if ($success) { Write-Log " Pushed: DEPLOY.BAT" $pushedFiles++ } else { Write-Log " ERROR: Failed to push DEPLOY.BAT" $errorCount++ } } } else { Write-Log " WARNING: DEPLOY.BAT not found at $deployBatLocal" } # Sync per-station ProdSW folders Write-Log "Syncing station-specific ProdSW folders..." $stationFolders = Get-ChildItem -Path $AD2_TEST_PATH -Directory -Filter "TS-*" -ErrorAction SilentlyContinue foreach ($station in $stationFolders) { $prodSwPath = Join-Path $station.FullName "ProdSW" if (Test-Path $prodSwPath) { # Get all files in ProdSW (including subdirectories) $prodSwFiles = Get-ChildItem -Path $prodSwPath -File -Recurse -ErrorAction SilentlyContinue foreach ($file in $prodSwFiles) { # Calculate relative path from ProdSW folder $relativePath = $file.FullName.Substring($prodSwPath.Length + 1).Replace('\', '/') $remotePath = "$NAS_DATA_PATH/$($station.Name)/ProdSW/$relativePath" if ($DryRun) { Write-Log " [DRY RUN] Would push: $($station.Name)/ProdSW/$relativePath" $pushedFiles++ } else { $success = Copy-ToNAS -LocalPath $file.FullName -RemotePath $remotePath if ($success) { Write-Log " Pushed: $($station.Name)/ProdSW/$relativePath" $pushedFiles++ } else { Write-Log " ERROR: Failed to push $($station.Name)/ProdSW/$relativePath" $errorCount++ } } } } # Check for TODO.BAT (one-time task file) $todoBatPath = Join-Path $station.FullName "TODO.BAT" if (Test-Path $todoBatPath) { $remoteTodoPath = "$NAS_DATA_PATH/$($station.Name)/TODO.BAT" Write-Log "Found TODO.BAT for $($station.Name)" if ($DryRun) { Write-Log " [DRY RUN] Would push TODO.BAT -> $remoteTodoPath" $pushedFiles++ } else { $success = Copy-ToNAS -LocalPath $todoBatPath -RemotePath $remoteTodoPath if ($success) { Write-Log " Pushed TODO.BAT to NAS" # Remove from AD2 after successful push (one-shot mechanism) Remove-Item -Path $todoBatPath -Force Write-Log " Removed TODO.BAT from AD2 (pushed to NAS)" $pushedFiles++ } else { Write-Log " ERROR: Failed to push TODO.BAT" $errorCount++ } } } } Write-Log "AD2 to NAS sync: $pushedFiles file(s) pushed" # ============================================================================ # Update Status File # ============================================================================ $status = if ($errorCount -eq 0) { "OK" } else { "ERRORS" } $statusContent = @" AD2 <-> NAS Bidirectional Sync Status ====================================== Timestamp: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Status: $status PULL (NAS -> AD2 - Test Results): Files Pulled: $syncedFiles Files Skipped: $skippedFiles DAT Files Imported to DB: $($syncedDatFiles.Count) PUSH (AD2 -> NAS - Software Updates): Files Pushed: $pushedFiles Errors: $errorCount "@ Set-Content -Path $STATUS_FILE -Value $statusContent Write-Log "==========================================" Write-Log "Sync complete: PULL=$syncedFiles, PUSH=$pushedFiles, Errors=$errorCount" Write-Log "==========================================" # Exit with error code if there were failures if ($errorCount -gt 0) { exit 1 } else { exit 0 }