Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/FileStorageController.cs

684 lines
24 KiB
C#

using FruitBank.Common.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage;
using Nop.Services.Security;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.FruitBank.Controllers
{
[AuthorizeAdmin]
[Area(AreaNames.ADMIN)]
[AutoValidateAntiforgeryToken]
public class FileStorageController : BasePluginController
{
private readonly FileStorageService _fileStorageService;
private readonly FruitBankDbContext _dbContext;
private readonly IPermissionService _permissionService;
private readonly IWorkContext _workContext;
public FileStorageController(
FileStorageService fileStorageService,
FruitBankDbContext dbContext,
IPermissionService permissionService,
IWorkContext workContext)
{
_fileStorageService = fileStorageService;
_dbContext = dbContext;
_permissionService = permissionService;
_workContext = workContext;
}
#region Upload Files
/// <summary>
/// Upload a single file
/// </summary>
/// <param name="file">The uploaded file</param>
/// <param name="featureName">Feature name (e.g., "AIdocumentprocessing")</param>
/// <param name="entityType">Entity type (e.g., "ShippingDocuments")</param>
/// <param name="entityId">Entity ID</param>
/// <param name="rawText">Optional raw text for searchable documents</param>
[HttpPost]
public async Task<IActionResult> UploadFile(
IFormFile file,
string featureName,
string entityType,
int entityId,
string rawText = null)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
if (file == null || file.Length == 0)
return Json(new { success = false, message = "No file uploaded" });
if (string.IsNullOrWhiteSpace(featureName))
return Json(new { success = false, message = "Feature name is required" });
if (string.IsNullOrWhiteSpace(entityType))
return Json(new { success = false, message = "Entity type is required" });
if (entityId <= 0)
return Json(new { success = false, message = "Valid entity ID is required" });
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
using (var stream = file.OpenReadStream())
{
var fileEntity = await _fileStorageService.SaveFileAsync(
fileStream: stream,
fileName: file.FileName,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId,
rawText: rawText
);
return Json(new
{
success = true,
message = "File uploaded successfully",
file = new
{
id = fileEntity.Id,
fileName = fileEntity.FileName,
fileExtension = fileEntity.FileExtension,
created = fileEntity.Created,
hasRawText = !string.IsNullOrEmpty(fileEntity.RawText)
}
});
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error uploading file: {ex}");
return Json(new
{
success = false,
message = $"Error uploading file: {ex.Message}"
});
}
}
/// <summary>
/// Upload multiple files at once
/// </summary>
[HttpPost]
public async Task<IActionResult> UploadMultipleFiles(
List<IFormFile> files,
string featureName,
string entityType,
int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
if (files == null || files.Count == 0)
return Json(new { success = false, message = "No files uploaded" });
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
var uploadedFiles = new List<object>();
var errors = new List<string>();
foreach (var file in files)
{
try
{
if (file.Length > 0)
{
using (var stream = file.OpenReadStream())
{
var fileEntity = await _fileStorageService.SaveFileAsync(
fileStream: stream,
fileName: file.FileName,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId
);
uploadedFiles.Add(new
{
id = fileEntity.Id,
fileName = fileEntity.FileName + fileEntity.FileExtension
});
}
}
}
catch (Exception ex)
{
errors.Add($"{file.FileName}: {ex.Message}");
}
}
return Json(new
{
success = uploadedFiles.Count > 0,
message = $"Uploaded {uploadedFiles.Count} of {files.Count} files",
files = uploadedFiles,
errors = errors
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error uploading multiple files: {ex}");
return Json(new
{
success = false,
message = $"Error: {ex.Message}"
});
}
}
#endregion
#region Download Files
/// <summary>
/// Download a file by ID
/// </summary>
[HttpGet]
public async Task<IActionResult> DownloadFile(
int fileId,
string featureName,
string entityType,
int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Unauthorized();
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
var (fileStream, fileInfo) = await _fileStorageService.GetFileByIdAsync(
fileId: fileId,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId
);
var fileName = $"{fileInfo.FileName}{fileInfo.FileExtension}";
var contentType = GetContentType(fileInfo.FileExtension);
return File(fileStream, contentType, fileName);
}
catch (FileNotFoundException ex)
{
return NotFound(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error downloading file: {ex}");
return BadRequest(new { success = false, message = ex.Message });
}
}
/// <summary>
/// Preview a file inline (for PDFs, images)
/// </summary>
[HttpGet]
public async Task<IActionResult> PreviewFile(
int fileId,
string featureName,
string entityType,
int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Unauthorized();
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
var (fileStream, fileInfo) = await _fileStorageService.GetFileByIdAsync(
fileId: fileId,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId
);
var contentType = GetContentType(fileInfo.FileExtension);
// Return inline for preview
return File(fileStream, contentType);
}
catch (FileNotFoundException ex)
{
return NotFound(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error previewing file: {ex}");
return BadRequest(new { success = false, message = ex.Message });
}
}
#endregion
#region List & Search Files
/// <summary>
/// Get all files
/// </summary>
[HttpGet]
public async Task<IActionResult> GetAllFiles()
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
try
{
var files = await _fileStorageService.GetAllFilesAsync();
return Json(new
{
success = true,
count = files.Count,
files = files.Select(f => new
{
id = f.Id,
fileName = f.FileName,
fileExtension = f.FileExtension,
fullName = $"{f.FileName}{f.FileExtension}",
created = f.Created,
modified = f.Modified,
hasRawText = !string.IsNullOrEmpty(f.RawText)
})
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error getting files: {ex}");
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// Search files by filename or content
/// </summary>
[HttpGet]
public async Task<IActionResult> SearchFiles(string searchTerm)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
if (string.IsNullOrWhiteSpace(searchTerm))
return Json(new { success = false, message = "Search term is required" });
try
{
var files = await _fileStorageService.SearchFilesAsync(searchTerm);
return Json(new
{
success = true,
searchTerm = searchTerm,
count = files.Count,
files = files.Select(f => new
{
id = f.Id,
fileName = f.FileName,
fileExtension = f.FileExtension,
fullName = $"{f.FileName}{f.FileExtension}",
created = f.Created,
hasRawText = !string.IsNullOrEmpty(f.RawText),
// Include snippet of matched text if available
textSnippet = GetTextSnippet(f.RawText, searchTerm)
})
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error searching files: {ex}");
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// Get files for a specific entity
/// </summary>
[HttpGet]
public async Task<IActionResult> GetEntityFiles(string entityType, int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
try
{
// Example for ShippingDocuments - adapt for other entity types
if (entityType == "ShippingDocuments")
{
var mappings = await _dbContext.ShippingDocumentToFiles
.GetAll()
.Where(m => m.ShippingDocumentId == entityId)
.ToListAsync();
var fileIds = mappings.Select(m => m.FilesId).ToList();
var files = await _dbContext.Files
.GetAll()
.Where(f => fileIds.Contains(f.Id))
.ToListAsync();
return Json(new
{
success = true,
entityType = entityType,
entityId = entityId,
count = files.Count,
files = files.Select(f => new
{
id = f.Id,
fileName = $"{f.FileName}{f.FileExtension}",
created = f.Created,
documentType = mappings.FirstOrDefault(m => m.FilesId == f.Id)?.DocumentType.ToString()
})
});
}
return Json(new
{
success = false,
message = $"Entity type '{entityType}' not supported yet"
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error getting entity files: {ex}");
return Json(new { success = false, message = ex.Message });
}
}
#endregion
#region Delete Files
/// <summary>
/// Delete a file by ID
/// </summary>
[HttpPost]
public async Task<IActionResult> DeleteFile(
int fileId,
string featureName,
string entityType,
int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
var deleted = await _fileStorageService.DeleteFileAsync(
fileId: fileId,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId
);
if (deleted)
{
// Also delete mappings (example for ShippingDocuments)
if (entityType == "ShippingDocuments")
{
var mappings = await _dbContext.ShippingDocumentToFiles
.GetAll()
.Where(m => m.FilesId == fileId && m.ShippingDocumentId == entityId)
.ToListAsync();
foreach (var mapping in mappings)
{
await _dbContext.ShippingDocumentToFiles.DeleteAsync(mapping);
}
}
}
return Json(new
{
success = deleted,
message = deleted ? "File deleted successfully" : "File not found"
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error deleting file: {ex}");
return Json(new
{
success = false,
message = $"Error deleting file: {ex.Message}"
});
}
}
/// <summary>
/// Delete multiple files at once
/// </summary>
[HttpPost]
public async Task<IActionResult> DeleteMultipleFiles(
List<int> fileIds,
string featureName,
string entityType,
int entityId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
if (fileIds == null || fileIds.Count == 0)
return Json(new { success = false, message = "No file IDs provided" });
try
{
var currentUser = await _workContext.GetCurrentCustomerAsync();
var userId = currentUser.Id;
var deletedCount = 0;
var errors = new List<string>();
foreach (var fileId in fileIds)
{
try
{
var deleted = await _fileStorageService.DeleteFileAsync(
fileId: fileId,
userId: userId,
featureName: featureName,
entityType: entityType,
entityId: entityId
);
if (deleted)
{
deletedCount++;
}
}
catch (Exception ex)
{
errors.Add($"File {fileId}: {ex.Message}");
}
}
return Json(new
{
success = deletedCount > 0,
message = $"Deleted {deletedCount} of {fileIds.Count} files",
deletedCount = deletedCount,
errors = errors
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error deleting multiple files: {ex}");
return Json(new
{
success = false,
message = $"Error: {ex.Message}"
});
}
}
#endregion
#region File Information
/// <summary>
/// Get file information by ID
/// </summary>
[HttpGet]
public async Task<IActionResult> GetFileInfo(int fileId)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
try
{
var fileEntity = await _dbContext.Files.GetByIdAsync(fileId);
if (fileEntity == null)
return NotFound(new { success = false, message = "File not found" });
return Json(new
{
success = true,
file = new
{
id = fileEntity.Id,
fileName = fileEntity.FileName,
fileExtension = fileEntity.FileExtension,
fullName = $"{fileEntity.FileName}{fileEntity.FileExtension}",
created = fileEntity.Created,
modified = fileEntity.Modified,
hasRawText = !string.IsNullOrEmpty(fileEntity.RawText),
rawTextLength = fileEntity.RawText?.Length ?? 0
}
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error getting file info: {ex}");
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// Update file metadata (RawText)
/// </summary>
[HttpPost]
public async Task<IActionResult> UpdateFileMetadata(int fileId, string rawText)
{
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
return Json(new { success = false, message = "Access denied" });
try
{
var fileEntity = await _dbContext.Files.GetByIdAsync(fileId);
if (fileEntity == null)
return NotFound(new { success = false, message = "File not found" });
fileEntity.RawText = rawText;
await _fileStorageService.AddOrUpdateFileAsync(fileEntity);
return Json(new
{
success = true,
message = "File metadata updated successfully"
});
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error updating file metadata: {ex}");
return Json(new
{
success = false,
message = $"Error updating metadata: {ex.Message}"
});
}
}
#endregion
#region Helper Methods
/// <summary>
/// Get content type based on file extension
/// </summary>
private string GetContentType(string extension)
{
return extension.ToLowerInvariant() switch
{
".pdf" => "application/pdf",
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".webp" => "image/webp",
".bmp" => "image/bmp",
".txt" => "text/plain",
".csv" => "text/csv",
".json" => "application/json",
".xml" => "application/xml",
".html" => "text/html",
".doc" => "application/msword",
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".xls" => "application/vnd.ms-excel",
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".zip" => "application/zip",
".rar" => "application/x-rar-compressed",
_ => "application/octet-stream"
};
}
/// <summary>
/// Get a snippet of text around the search term
/// </summary>
private string GetTextSnippet(string text, string searchTerm, int contextLength = 100)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm))
return null;
var index = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
if (index == -1)
return null;
var start = Math.Max(0, index - contextLength);
var length = Math.Min(text.Length - start, contextLength * 2 + searchTerm.Length);
var snippet = text.Substring(start, length);
if (start > 0)
snippet = "..." + snippet;
if (start + length < text.Length)
snippet = snippet + "...";
return snippet;
}
#endregion
}
}