using Microsoft.AspNetCore.Mvc; using PSTW_CentralSystem.DBContext; using PSTW_CentralSystem.Areas.MMS.Models; using System.IO; using System.Linq; using PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator; using QuestPDF.Fluent; using System.Threading.Tasks; using System.Threading; using System.Collections.Generic; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using MySqlConnector; namespace PSTW_CentralSystem.Areas.MMS.Controllers { public class TarballPdfDto { // From tbl_marine_tarball public int Id { get; set; } public required string StationID { get; set; } public required string Longitude { get; set; } public required string Latitude { get; set; } public required DateTime DateSample { get; set; } public required TimeSpan TimeSample { get; set; } public required string ClassifyID { get; set; } public string? OptionalName1 { get; set; } public string? OptionalName2 { get; set; } public string? OptionalName3 { get; set; } public string? OptionalName4 { get; set; } public required string FirstSampler { get; set; } // From joined tables public required string LocationName { get; set; } // From tbl_marine_station public required string StateName { get; set; } // From tbl_state public required string FullName { get; set; } // From tbl_user public required string LevelName { get; set; } // From tbl_level } [Area("MMS")] public class MarineController : Controller { private readonly MMSSystemContext _context;//Used in TarBallForm and GeneratePdfResponse to query the database. private readonly NetworkShareAccess _networkAccessService;//used in GetImage and GeneratePdfResponse private const string PhotoBasePath = @"\\192.168.12.42\images\marine\manual_tarball";//used in GetImage and GeneratePdfResponse public MarineController(MMSSystemContext context, NetworkShareAccess networkAccessService) { _context = context; _networkAccessService = networkAccessService; } public IActionResult Index() { return View(); } public IActionResult TarBallForm() { try { var marineTarballs = _context.MarineTarballs .Select(t => new { id = t.Id, date = t.DateSample.ToString("yyyy/MM/dd"), station = t.StationID, time = t.TimeSample.ToString("hh\\:mm\\:ss") }) .ToList(); Console.WriteLine($"Marine Tarballs Count: {marineTarballs.Count}"); return View(marineTarballs); } catch (Exception ex) { return Content($"Error: {ex.Message}
{ex.StackTrace}", "text/html"); } } [HttpGet] // Explicitly mark as a GET endpoint public IActionResult TestCredentials() { try { // Use the EXACT same path/credentials as in Program.cs var testService = new NetworkShareAccess( @"\\192.168.12.42\images\marine\manual_tarball", "installer", "mms@pstw" ); testService.ConnectToNetworkPath(); testService.DisconnectFromNetworkShare(); return Ok("Network credentials and path are working correctly!"); } catch (Exception ex) { // Log the full error (including stack trace) Console.WriteLine($"TestCredentials failed: {ex}"); return StatusCode(500, $"Credentials test failed: {ex.Message}"); } } public IActionResult GetImage(string fileName) { if (string.IsNullOrEmpty(fileName)) { return BadRequest("Filename cannot be empty"); } // Sanitize filename to prevent path traversal attacks var sanitizedFileName = Path.GetFileName(fileName); if (sanitizedFileName != fileName) { return BadRequest("Invalid filename"); } int retryCount = 0; const int maxRetries = 3; bool connectionSuccess = false; // Retry loop for network connection while (retryCount < maxRetries && !connectionSuccess) { try { Console.WriteLine($"Attempt {retryCount + 1} to connect to network share..."); // Connect to network share _networkAccessService.ConnectToNetworkPath(); connectionSuccess = true; Console.WriteLine("Network share connected successfully"); } catch (Exception ex) { retryCount++; Console.WriteLine($"Connection attempt {retryCount} failed: {ex.Message}"); if (retryCount >= maxRetries) { Console.WriteLine($"Max connection attempts reached. Last error: {ex}"); return StatusCode(503, $"Could not establish connection to image server after {maxRetries} attempts"); } // Wait before retrying (1s, 2s, 3s) Thread.Sleep(1000 * retryCount); } } try { string imagePath = Path.Combine(PhotoBasePath, sanitizedFileName); Console.WriteLine($"Attempting to access image at: {imagePath}"); // Verify file exists if (!System.IO.File.Exists(imagePath)) { Console.WriteLine($"Image not found at path: {imagePath}"); return NotFound($"Image '{sanitizedFileName}' not found on server"); } // Verify file is an image if (!IsImageValid(imagePath)) { Console.WriteLine($"Invalid image file at path: {imagePath}"); return BadRequest("The requested file is not a valid image"); } // Read and return the image byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath); Console.WriteLine($"Successfully read image: {sanitizedFileName} ({imageBytes.Length} bytes)"); // Determine content type based on extension string contentType = "image/jpeg"; // default string extension = Path.GetExtension(sanitizedFileName).ToLower(); //string extension = Path.GetExtension(sanitizedFileName)?.ToLower(); if (extension == ".png") { contentType = "image/png"; } else if (extension == ".gif") { contentType = "image/gif"; } return File(imageBytes, contentType); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"Access denied to image: {ex}"); return StatusCode(403, "Access to the image was denied"); } catch (IOException ex) { Console.WriteLine($"IO error accessing image: {ex}"); return StatusCode(503, "Error accessing image file"); } catch (Exception ex) { Console.WriteLine($"Unexpected error: {ex}"); return StatusCode(500, "An unexpected error occurred while processing the image"); } finally { try { if (connectionSuccess) { Console.WriteLine("Disconnecting from network share..."); _networkAccessService.DisconnectFromNetworkShare(); } } catch (Exception ex) { Console.WriteLine($"Warning: Error disconnecting from share: {ex.Message}"); // Don't fail the request because of disconnect issues } } } public async Task GenerateReport(int id)//calls GeneratePdfResponse to generate a PDF for inline viewing { return await GeneratePdfResponse(id, true); } public async Task DownloadPDF(int id) { return await GeneratePdfResponse(id, true); } public IActionResult ViewPDF(int id) { try { // Add timeout for safety var task = Task.Run(() => GeneratePdfResponse(id, false)); if (task.Wait(TimeSpan.FromSeconds(30))) // 30 second timeout { return task.Result; } return StatusCode(500, "PDF generation took too long"); } catch (Exception ex) { Console.WriteLine($"PDF VIEW ERROR: {ex}"); return StatusCode(500, $"Error showing PDF: {ex.Message}"); } } private async Task GeneratePdfResponse(int id, bool forceDownload) { Console.WriteLine($"Requested ID in {(forceDownload ? "GenerateReport" : "ViewPDF")}: {id}"); // Test network connection first try { _networkAccessService.ConnectToNetworkPath(); _networkAccessService.DisconnectFromNetworkShare(); } catch (Exception ex) { return StatusCode(500, $"Cannot access network: {ex.Message}"); } try { _networkAccessService.ConnectToNetworkPath(); // ===== 1. Get Data from Database ===== var query = @" SELECT marine.*, station.LocationName, state.StateName, user.FullName, level.LevelName FROM tbl_marine_tarball marine JOIN tbl_marine_station station ON marine.StationID = station.StationID JOIN tbl_state state ON station.StateID = state.StateID JOIN tbl_user user ON marine.FirstSampler = user.FullName JOIN tbl_level level ON user.LevelID = level.LevelID WHERE marine.Id = @id"; var tarball = await _context.Database .SqlQueryRaw(query, new MySqlParameter("@id", id)) .FirstOrDefaultAsync(); if (tarball == null) return NotFound("Record not found"); // Prepare boolean values for PDF bool tarBallYes = tarball.ClassifyID != "NO"; bool tarBallNo = tarball.ClassifyID == "NO"; bool isSand = tarball.ClassifyID == "SD"; bool isNonSandy = tarball.ClassifyID == "NS"; bool isCoquina = tarball.ClassifyID == "CO"; // ===== 2. Get Images ===== // For date (stored as DATE in DB → "2025-01-30" becomes "20250130") var sampleDateString = tarball.DateSample.ToString("yyyyMMdd"); // For time (stored as TIME in DB → "16:49:02" becomes "164902") var sampleTimeString = tarball.TimeSample.ToString("hhmmss"); var stationFolder = Path.Combine(PhotoBasePath, tarball.StationID); var stationImages = new Dictionary(); var foundAnyImages = false; if (Directory.Exists(stationFolder)) { var basePattern = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}_"; var allImages = Directory.GetFiles(stationFolder, $"{basePattern}*"); foreach (var imagePath in allImages) { var fileName = Path.GetFileNameWithoutExtension(imagePath); var type = fileName.Split('_').Last(); stationImages[type] = imagePath; foundAnyImages = true; } } if (!foundAnyImages) { return StatusCode(404, "No images found for this record"); } // Verify mandatory images exist var mandatoryImages = new[] { "LEFTSIDECOASTALVIEW", "RIGHTSIDECOASTALVIEW", "DRAWINGVERTICALLINES", "DRAWINGHORIZONTALLINES" }; foreach (var type in mandatoryImages) { if (!stationImages.ContainsKey(type)) { return StatusCode(400, $"Missing mandatory image: {type}"); } } // ===== 3. Generate PDF ===== var pdf = new TarBallPDF( tarball.StateName, tarball.StationID, tarball.LocationName, tarball.Longitude, tarball.Latitude, tarball.DateSample, tarball.TimeSample, tarball.ClassifyID, tarBallYes, tarBallNo, isSand, isNonSandy, isCoquina, stationImages["LEFTSIDECOASTALVIEW"], stationImages["RIGHTSIDECOASTALVIEW"], stationImages["DRAWINGVERTICALLINES"], stationImages["DRAWINGHORIZONTALLINES"], stationImages.GetValueOrDefault("OPTIONAL01"), stationImages.GetValueOrDefault("OPTIONAL02"), stationImages.GetValueOrDefault("OPTIONAL03"), stationImages.GetValueOrDefault("OPTIONAL04"), tarball.OptionalName1, tarball.OptionalName2, tarball.OptionalName3, tarball.OptionalName4, tarball.FirstSampler, tarball.FullName, tarball.LevelName ).GeneratePdf(); // ===== 4. Return PDF ===== var downloadName = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}.pdf"; return forceDownload ? File(pdf, "application/pdf", downloadName) : File(pdf, "application/pdf"); } catch (Exception ex) { return StatusCode(500, $"PDF generation failed: {ex.Message}"); } finally { _networkAccessService.DisconnectFromNetworkShare(); } } private bool IsImageValid(string imagePath) { try { using (var image = System.Drawing.Image.FromFile(imagePath)) return true; } catch { Console.WriteLine($"Invalid image skipped: {imagePath}"); return false; } } } }