# Run FTP upload for every practice folder found under this root. # Searches recursively for ftp-config.ini and uploads PDFs from today's date folder in each. $rootDir = Split-Path -Parent $MyInvocation.MyCommand.Path $today = Get-Date -Format "yyyyMMdd" $configs = Get-ChildItem -Path $rootDir -Recurse -Filter "ftp-config.ini" if ($configs.Count -eq 0) { Write-Host "No ftp-config.ini files found under $rootDir" -ForegroundColor Yellow exit 0 } Write-Host "" Write-Host "Date: $today" Write-Host "Found $($configs.Count) practice folder(s)" -ForegroundColor Cyan Write-Host ("=" * 50) $totalOk = 0 $totalFail = 0 $totalSkip = 0 foreach ($configFile in $configs) { $folderDir = $configFile.DirectoryName $folderName = Split-Path -Leaf $folderDir $dateDir = Join-Path $folderDir $today Write-Host "" Write-Host "[ $folderName ]" -ForegroundColor Cyan if (-not (Test-Path $dateDir)) { Write-Host " No folder for today ($today) — skipping." -ForegroundColor Gray $totalSkip++ continue } # Parse config $config = @{} foreach ($line in Get-Content $configFile.FullName) { if ($line -match '^\s*([^=;#\s]+)\s*=\s*(.+?)\s*$') { $config[$matches[1]] = $matches[2] } } $ftpHost = $config["host"] $ftpPort = if ($config["port"]) { [int]$config["port"] } else { 21 } $username = $config["username"] $password = $config["password"] $remotePath = if ($config["remote_path"]) { $config["remote_path"].TrimEnd("/") } else { "/" } $useTls = $config["tls"] -eq "true" if (-not $ftpHost -or -not $username -or -not $password) { Write-Host " SKIPPED: ftp-config.ini missing required fields (host, username, password)" -ForegroundColor Yellow $totalSkip++ continue } $archiveDir = Join-Path $folderDir "archive" if (-not (Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir | Out-Null } $pdfs = Get-ChildItem -Path $dateDir -Filter "*.pdf" -File if ($pdfs.Count -eq 0) { Write-Host " No PDFs in $today\ — skipping." -ForegroundColor Gray $totalSkip++ continue } $ok = 0 $fail = 0 foreach ($pdf in $pdfs) { $remoteFile = "$remotePath/$($pdf.Name)" $uri = "ftp://${ftpHost}:${ftpPort}${remoteFile}" Write-Host " $($pdf.Name) ..." -NoNewline try { $req = [System.Net.FtpWebRequest]::Create($uri) $req.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile $req.Credentials = New-Object System.Net.NetworkCredential($username, $password) $req.EnableSsl = $useTls $req.UseBinary = $true $req.UsePassive = $true $req.KeepAlive = $false $bytes = [System.IO.File]::ReadAllBytes($pdf.FullName) $req.ContentLength = $bytes.Length $stream = $req.GetRequestStream() $stream.Write($bytes, 0, $bytes.Length) $stream.Close() $resp = $req.GetResponse() $resp.Close() Write-Host " uploaded" -ForegroundColor Green $ok++ } catch { Write-Host " FAILED: $($_.Exception.Message)" -ForegroundColor Red $fail++ } } $totalOk += $ok $totalFail += $fail if ($fail -eq 0 -and $ok -gt 0) { Move-Item -Path $dateDir -Destination (Join-Path $archiveDir $today) -Force Write-Host " $ok file(s) uploaded. Folder $today\ archived." -ForegroundColor Green } elseif ($fail -gt 0) { Write-Host " $ok uploaded, $fail failed (left in $today\ for retry)." -ForegroundColor Yellow } } Write-Host "" Write-Host ("=" * 50) if ($totalFail -eq 0) { Write-Host "All done: $totalOk file(s) uploaded across $($configs.Count - $totalSkip) folder(s)." -ForegroundColor Green } else { Write-Host "Done: $totalOk uploaded, $totalFail failed, $totalSkip skipped." -ForegroundColor Yellow }