# Quality Label Printing Service - PowerShell Implementation # Native Windows solution with no external dependencies param( [int]$Port = 8765, [string]$LogFile = "$env:ProgramFiles\QualityLabelPrinting\PrintService\print_service.log" ) # Ensure log directory exists $logDir = Split-Path $LogFile -Parent if (!(Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } # Logging function function Write-ServiceLog { param([string]$Message) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMessage = "[$timestamp] $Message" Write-Host $logMessage Add-Content -Path $LogFile -Value $logMessage -ErrorAction SilentlyContinue } # Get available printers function Get-AvailablePrinters { try { $printers = Get-WmiObject -Class Win32_Printer | Where-Object { $_.Local -eq $true } | ForEach-Object { @{ name = $_.Name driver = $_.DriverName port = $_.PortName is_default = $_.Default status = $_.PrinterStatus } } return @{ success = $true printers = $printers count = $printers.Count } } catch { Write-ServiceLog "Error getting printers: $($_.Exception.Message)" return @{ success = $false error = $_.Exception.Message printers = @() } } } # Print PDF function function Invoke-PrintPDF { param( [string]$PdfUrl, [string]$PrinterName = "default", [int]$Copies = 1 ) try { Write-ServiceLog "Print request: URL=$PdfUrl, Printer=$PrinterName, Copies=$Copies" # Download PDF to temp file $tempFile = [System.IO.Path]::GetTempFileName() + ".pdf" $webClient = New-Object System.Net.WebClient $webClient.DownloadFile($PdfUrl, $tempFile) Write-ServiceLog "PDF downloaded to: $tempFile" # Get default printer if needed if ($PrinterName -eq "default" -or [string]::IsNullOrEmpty($PrinterName)) { $defaultPrinter = Get-WmiObject -Class Win32_Printer | Where-Object { $_.Default -eq $true } $PrinterName = $defaultPrinter.Name } # Print using Windows shell $printJob = Start-Process -FilePath $tempFile -Verb Print -PassThru -WindowStyle Hidden Start-Sleep -Seconds 2 # Clean up temp file Remove-Item $tempFile -Force -ErrorAction SilentlyContinue Write-ServiceLog "Print job sent successfully to printer: $PrinterName" return @{ success = $true message = "Print job sent successfully" printer = $PrinterName timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } } catch { Write-ServiceLog "Print error: $($_.Exception.Message)" return @{ success = $false error = $_.Exception.Message } } } # Print local PDF file function function Invoke-PrintLocalPDF { param( [string]$PdfPath, [string]$PrinterName = "default", [int]$Copies = 1 ) try { Write-ServiceLog "Local print request: File=$PdfPath, Printer=$PrinterName, Copies=$Copies" # Check if file exists if (!(Test-Path $PdfPath)) { throw "PDF file not found: $PdfPath" } # Get default printer if needed if ($PrinterName -eq "default" -or [string]::IsNullOrEmpty($PrinterName)) { $defaultPrinter = Get-WmiObject -Class Win32_Printer | Where-Object { $_.Default -eq $true } $PrinterName = $defaultPrinter.Name } Write-ServiceLog "Using printer: $PrinterName" # Print using Windows shell $printJob = Start-Process -FilePath $PdfPath -Verb Print -PassThru -WindowStyle Hidden Start-Sleep -Seconds 2 Write-ServiceLog "Print job sent successfully to printer: $PrinterName" return @{ success = $true message = "Print job sent successfully" printer = $PrinterName timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } } catch { Write-ServiceLog "Print error: $($_.Exception.Message)" return @{ success = $false error = $_.Exception.Message } } } # HTTP Response function function Send-HttpResponse { param( [System.Net.HttpListenerContext]$Context, [int]$StatusCode = 200, [string]$ContentType = "application/json", [string]$Body = "" ) try { $Context.Response.StatusCode = $StatusCode $Context.Response.ContentType = "$ContentType; charset=utf-8" # Add comprehensive CORS headers $Context.Response.Headers.Add("Access-Control-Allow-Origin", "*") $Context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE") $Context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Origin, X-Requested-With") $Context.Response.Headers.Add("Access-Control-Max-Age", "86400") if ($Body) { $buffer = [System.Text.Encoding]::UTF8.GetBytes($Body) $Context.Response.ContentLength64 = $buffer.Length $Context.Response.OutputStream.Write($buffer, 0, $buffer.Length) } $Context.Response.OutputStream.Close() } catch { Write-ServiceLog "Error sending response: $($_.Exception.Message)" } } # Main HTTP server function function Start-PrintService { Write-ServiceLog "Starting Quality Label Printing Service on port $Port" try { # Create HTTP listener $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://localhost:$Port/") $listener.Prefixes.Add("http://127.0.0.1:$Port/") $listener.Start() Write-ServiceLog "HTTP server started on http://localhost:$Port" # Main server loop while ($listener.IsListening) { try { # Wait for request $context = $listener.GetContext() $request = $context.Request $response = $context.Response $method = $request.HttpMethod $url = $request.Url.AbsolutePath Write-ServiceLog "$method $url" # Handle CORS preflight requests first if ($method -eq "OPTIONS") { Write-ServiceLog "Handling CORS preflight request for $url" Send-HttpResponse -Context $context -StatusCode 200 continue } # Handle different endpoints switch -Regex ($url) { "^/health$" { $healthData = @{ status = "healthy" service = "Quality Label Printing Service" version = "1.0" timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" platform = "Windows PowerShell" } Send-HttpResponse -Context $context -Body ($healthData | ConvertTo-Json) } "^/printers$" { $printersData = Get-AvailablePrinters Send-HttpResponse -Context $context -Body ($printersData | ConvertTo-Json -Depth 3) } "^/print/(pdf|silent|file)$" { if ($method -eq "POST") { try { # Read request body $reader = New-Object System.IO.StreamReader($request.InputStream) $body = $reader.ReadToEnd() $reader.Close() # Parse JSON $printData = $body | ConvertFrom-Json # Print PDF - handle both URL and local file path if ($printData.pdf_path -and (Test-Path $printData.pdf_path)) { Write-ServiceLog "Using local PDF file: $($printData.pdf_path)" $result = Invoke-PrintLocalPDF -PdfPath $printData.pdf_path -PrinterName $printData.printer_name -Copies $printData.copies } elseif ($printData.pdf_url) { Write-ServiceLog "Using PDF URL: $($printData.pdf_url)" $result = Invoke-PrintPDF -PdfUrl $printData.pdf_url -PrinterName $printData.printer_name -Copies $printData.copies } else { throw "Either pdf_path or pdf_url must be provided" } if ($result.success) { Send-HttpResponse -Context $context -Body ($result | ConvertTo-Json) } else { Send-HttpResponse -Context $context -StatusCode 500 -Body ($result | ConvertTo-Json) } } catch { $errorResponse = @{ success = $false error = "Invalid request: $($_.Exception.Message)" } Send-HttpResponse -Context $context -StatusCode 400 -Body ($errorResponse | ConvertTo-Json) } } else { $errorResponse = @{ success = $false error = "Method not allowed" } Send-HttpResponse -Context $context -StatusCode 405 -Body ($errorResponse | ConvertTo-Json) } } default { $errorResponse = @{ success = $false error = "Endpoint not found" available_endpoints = @("/health", "/printers", "/print/pdf", "/print/silent", "/print/file") } Send-HttpResponse -Context $context -StatusCode 404 -Body ($errorResponse | ConvertTo-Json) } } } catch { Write-ServiceLog "Request error: $($_.Exception.Message)" try { $errorResponse = @{ success = $false error = "Internal server error" } Send-HttpResponse -Context $context -StatusCode 500 -Body ($errorResponse | ConvertTo-Json) } catch { # Ignore response errors } } } } catch { Write-ServiceLog "Fatal error: $($_.Exception.Message)" } finally { if ($listener) { $listener.Stop() $listener.Close() } Write-ServiceLog "Print service stopped" } } # Service entry point Write-ServiceLog "Quality Label Printing Service starting..." # Handle service stop gracefully Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Write-ServiceLog "Service shutting down..." } # Start the service try { Start-PrintService } catch { Write-ServiceLog "Service failed to start: $($_.Exception.Message)" exit 1 }