Implemented functionality to whitelist file extensions during upload to S3
This commit is contained in:
parent
eee07fa409
commit
fc050631e7
@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
public class AddCommentDto
|
public class AddCommentDto
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.Empty;
|
|
||||||
public Guid TicketId { get; set; } = Guid.Empty;
|
public Guid TicketId { get; set; } = Guid.Empty;
|
||||||
public Guid AuthorId { get; set; }
|
public Guid AuthorId { get; set; }
|
||||||
public string MessageText { get; set; } = string.Empty;
|
public string MessageText { get; set; } = string.Empty;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
public class CreateTicketDto
|
public class CreateTicketDto
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.Empty;
|
|
||||||
public string Subject { get; set; } = string.Empty;
|
public string Subject { get; set; } = string.Empty;
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
public Guid StatusId { get; set; }
|
public Guid StatusId { get; set; }
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
public class ForumAttachmentDto
|
public class ForumAttachmentDto
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.Empty;
|
|
||||||
public Guid TicketId { get; set; } = Guid.Empty;
|
public Guid TicketId { get; set; } = Guid.Empty;
|
||||||
public Guid? CommentId { get; set; }
|
public Guid? CommentId { get; set; }
|
||||||
public string FileName { get; set; } = string.Empty;
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
14
Marco.Pms.Model/Dtos/Forum/UpdateAttachmentDto.cs
Normal file
14
Marco.Pms.Model/Dtos/Forum/UpdateAttachmentDto.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Marco.Pms.Model.Dtos.Forum
|
||||||
|
{
|
||||||
|
public class UpdateAttachmentDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid TicketId { get; set; } = Guid.Empty;
|
||||||
|
public Guid? CommentId { get; set; }
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
public string? Base64Data { get; set; }
|
||||||
|
public int FileSize { get; set; }
|
||||||
|
public string ContentType { get; set; } = string.Empty;
|
||||||
|
public DateTime SentAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,6 @@
|
|||||||
public string MessageText { get; set; } = string.Empty;
|
public string MessageText { get; set; } = string.Empty;
|
||||||
public DateTime SentAt { get; set; }
|
public DateTime SentAt { get; set; }
|
||||||
public Guid? ParentMessageId { get; set; } // For threaded replies
|
public Guid? ParentMessageId { get; set; } // For threaded replies
|
||||||
public ICollection<ForumAttachmentDto>? Attachments { get; set; }
|
public ICollection<UpdateAttachmentDto>? Attachments { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
public int LinkedProjectId { get; set; }
|
public int LinkedProjectId { get; set; }
|
||||||
public int? LinkedActivityId { get; set; } // task or project ID
|
public int? LinkedActivityId { get; set; } // task or project ID
|
||||||
public ICollection<ForumAttachmentDto>? Attachments { get; set; }
|
public ICollection<UpdateAttachmentDto>? Attachments { get; set; }
|
||||||
public Guid PriorityId { get; set; }
|
public Guid PriorityId { get; set; }
|
||||||
public ICollection<Guid>? TagIds { get; set; }
|
public ICollection<Guid>? TagIds { get; set; }
|
||||||
public int TenantId { get; set; }
|
public int TenantId { get; set; }
|
||||||
|
@ -77,6 +77,16 @@ namespace Marco.Pms.Model.Mapper
|
|||||||
FileId = fileId,
|
FileId = fileId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public static TicketAttachment ToTicketAttachmentFromUpdateAttachmentDto(this UpdateAttachmentDto AttachmentDto, Guid ticketId, Guid fileId, Guid? commentId = null)
|
||||||
|
{
|
||||||
|
return new TicketAttachment
|
||||||
|
{
|
||||||
|
TicketId = ticketId,
|
||||||
|
CommentId = commentId,
|
||||||
|
FileName = AttachmentDto.FileName,
|
||||||
|
FileId = fileId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static Document ToDocumentFromForumAttachmentDto(this ForumAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt, int tenantId)
|
public static Document ToDocumentFromForumAttachmentDto(this ForumAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt, int tenantId)
|
||||||
{
|
{
|
||||||
@ -92,6 +102,20 @@ namespace Marco.Pms.Model.Mapper
|
|||||||
TenantId = tenantId
|
TenantId = tenantId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public static Document ToDocumentFromUpdateAttachmentDto(this UpdateAttachmentDto AttachmentDto, string objectKey, string thumbS3Key, DateTime uploadedAt, int tenantId)
|
||||||
|
{
|
||||||
|
return new Document
|
||||||
|
{
|
||||||
|
FileName = AttachmentDto.FileName,
|
||||||
|
ContentType = AttachmentDto.ContentType,
|
||||||
|
S3Key = objectKey,
|
||||||
|
ThumbS3Key = thumbS3Key,
|
||||||
|
Base64Data = AttachmentDto.Base64Data,
|
||||||
|
FileSize = AttachmentDto.FileSize,
|
||||||
|
UploadedAt = uploadedAt,
|
||||||
|
TenantId = tenantId
|
||||||
|
};
|
||||||
|
}
|
||||||
public static ForumTicketVM ToForumTicketVMFromTicketForum(this TicketForum ticket, Employee employee)
|
public static ForumTicketVM ToForumTicketVMFromTicketForum(this TicketForum ticket, Employee employee)
|
||||||
{
|
{
|
||||||
return new ForumTicketVM
|
return new ForumTicketVM
|
||||||
|
@ -57,7 +57,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
foreach (var attendanceLog in lstAttendance)
|
foreach (var attendanceLog in lstAttendance)
|
||||||
{
|
{
|
||||||
string objectKey = attendanceLog.Document.S3Key;
|
string objectKey = attendanceLog.Document.S3Key;
|
||||||
string preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
||||||
attendanceLogVMs.Add(attendanceLog.ToAttendanceLogVMFromAttendanceLog(preSignedUrl, preSignedUrl));
|
attendanceLogVMs.Add(attendanceLog.ToAttendanceLogVMFromAttendanceLog(preSignedUrl, preSignedUrl));
|
||||||
}
|
}
|
||||||
return Ok(ApiResponse<object>.SuccessResponse(attendanceLogVMs, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200));
|
return Ok(ApiResponse<object>.SuccessResponse(attendanceLogVMs, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200));
|
||||||
@ -509,33 +509,16 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
|
|
||||||
if (Image != null && Image.ContentType != null)
|
if (Image != null && Image.ContentType != null)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, TenantId, "Attendance");
|
||||||
}
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(Image.ContentType, TenantId, "Attendance");
|
|
||||||
objectKey = await _s3Service.UploadFileAsync(stream, fileName, Image.ContentType);
|
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
|
||||||
|
|
||||||
document = new Document
|
document = new Document
|
||||||
{
|
{
|
||||||
FileName = Image.FileName ?? fileName,
|
FileName = Image.FileName ?? "",
|
||||||
ContentType = Image.ContentType,
|
ContentType = Image.ContentType,
|
||||||
S3Key = objectKey,
|
S3Key = objectKey,
|
||||||
Base64Data = Image.Base64Data,
|
Base64Data = Image.Base64Data,
|
||||||
|
@ -62,34 +62,14 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
{
|
{
|
||||||
foreach (var attachmentDto in createTicketDto.Attachments)
|
foreach (var attachmentDto in createTicketDto.Attachments)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
|
||||||
var Image = attachmentDto;
|
var Image = attachmentDto;
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
{
|
{
|
||||||
_logger.LogError("Base64 data is missing");
|
_logger.LogError("Base64 data is missing");
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "Forum");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("{error}", ex.Message);
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(Image.ContentType, tenantId, string.Empty);
|
|
||||||
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, fileName, Image.ContentType);
|
|
||||||
|
|
||||||
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId);
|
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId);
|
||||||
_context.Documents.Add(document);
|
_context.Documents.Add(document);
|
||||||
@ -139,7 +119,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
||||||
}
|
}
|
||||||
@ -198,40 +178,20 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
{
|
{
|
||||||
if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.TicketId != updateTicketDto.Id)
|
if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.TicketId != updateTicketDto.Id)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
|
||||||
var Image = attachmentDto;
|
var Image = attachmentDto;
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
{
|
{
|
||||||
_logger.LogError("Base64 data is missing");
|
_logger.LogError("Base64 data is missing");
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "Forum");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("{error}", ex.Message);
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId);
|
||||||
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(Image.ContentType, tenantId, string.Empty);
|
|
||||||
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, fileName, Image.ContentType);
|
|
||||||
|
|
||||||
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId);
|
|
||||||
_context.Documents.Add(document);
|
_context.Documents.Add(document);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
var attachment = attachmentDto.ToTicketAttachmentFromForumAttachmentDto(ticketForum.Id, document.Id);
|
var attachment = attachmentDto.ToTicketAttachmentFromUpdateAttachmentDto(ticketForum.Id, document.Id);
|
||||||
attachments.Add(attachment);
|
attachments.Add(attachment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,7 +273,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
if (attachment.CommentId == null)
|
if (attachment.CommentId == null)
|
||||||
{
|
{
|
||||||
@ -376,33 +336,14 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
{
|
{
|
||||||
foreach (var attachmentDto in addCommentDto.Attachments)
|
foreach (var attachmentDto in addCommentDto.Attachments)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
|
||||||
var Image = attachmentDto;
|
var Image = attachmentDto;
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
{
|
{
|
||||||
_logger.LogError("Base64 data is missing");
|
_logger.LogError("Base64 data is missing");
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "Forum");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("{error}", ex.Message);
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(Image.ContentType, tenantId, string.Empty);
|
|
||||||
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, fileName, Image.ContentType);
|
|
||||||
|
|
||||||
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId);
|
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId);
|
||||||
_context.Documents.Add(document);
|
_context.Documents.Add(document);
|
||||||
@ -428,7 +369,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
||||||
}
|
}
|
||||||
@ -461,46 +402,31 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
|
|
||||||
List<TicketAttachment> existingAttachments = await _context.TicketAttachments.Where(a => a.CommentId == updateComment.Id).ToListAsync();
|
List<TicketAttachment> existingAttachments = await _context.TicketAttachments.Where(a => a.CommentId == updateComment.Id).ToListAsync();
|
||||||
var existingattachmentids = existingAttachments.Select(a => a.Id).ToList();
|
var existingattachmentids = existingAttachments.Select(a => a.Id).ToList();
|
||||||
var attachmentDtoids = updateCommentDto.Attachments.Select(a => a.Id).ToList();
|
List<Guid> attachmentDtoids = new List<Guid>();
|
||||||
|
if (updateCommentDto.Attachments != null)
|
||||||
foreach (var attachmentDto in updateCommentDto.Attachments)
|
|
||||||
{
|
{
|
||||||
if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.CommentId != updateComment.Id)
|
attachmentDtoids = updateCommentDto.Attachments.Select(a => a.Id).ToList();
|
||||||
|
|
||||||
|
foreach (var attachmentDto in updateCommentDto.Attachments)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.CommentId != updateComment.Id)
|
||||||
var Image = attachmentDto;
|
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
|
||||||
{
|
{
|
||||||
_logger.LogError("Base64 data is missing");
|
var Image = attachmentDto;
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
|
{
|
||||||
|
_logger.LogError("Base64 data is missing");
|
||||||
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, tenantId, "Forum");
|
||||||
|
|
||||||
|
Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId);
|
||||||
|
_context.Documents.Add(document);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var attachment = attachmentDto.ToTicketAttachmentFromUpdateAttachmentDto(existingComment.TicketId, document.Id, updateComment.Id);
|
||||||
|
attachments.Add(attachment);
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("{error}", ex.Message);
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(Image.ContentType, tenantId, string.Empty);
|
|
||||||
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, fileName, Image.ContentType);
|
|
||||||
|
|
||||||
Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId);
|
|
||||||
_context.Documents.Add(document);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
var attachment = attachmentDto.ToTicketAttachmentFromForumAttachmentDto(existingComment.TicketId, document.Id, updateComment.Id);
|
|
||||||
attachments.Add(attachment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attachments.Count != 0)
|
if (attachments.Count != 0)
|
||||||
@ -537,7 +463,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
|
||||||
}
|
}
|
||||||
@ -568,32 +494,13 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
|
|
||||||
foreach (var forumAttachmentDto in forumAttachmentDtos)
|
foreach (var forumAttachmentDto in forumAttachmentDtos)
|
||||||
{
|
{
|
||||||
byte[] fileBytes;
|
|
||||||
if (string.IsNullOrEmpty(forumAttachmentDto.Base64Data))
|
if (string.IsNullOrEmpty(forumAttachmentDto.Base64Data))
|
||||||
{
|
{
|
||||||
_logger.LogError("Base64 data is missing");
|
_logger.LogError("Base64 data is missing");
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = forumAttachmentDto.Base64Data.Contains(",")
|
|
||||||
? forumAttachmentDto.Base64Data.Substring(forumAttachmentDto.Base64Data.IndexOf(",") + 1)
|
|
||||||
: forumAttachmentDto.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
var objectKey = await _s3Service.UploadFileAsync(forumAttachmentDto.Base64Data, tenantId, "Forum");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("{error}", ex.Message);
|
|
||||||
return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, ex, 400)); ;
|
|
||||||
}
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
|
|
||||||
|
|
||||||
string fileName = _s3Service.GenerateFileName(forumAttachmentDto.ContentType, tenantId, string.Empty);
|
|
||||||
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, fileName, forumAttachmentDto.ContentType);
|
|
||||||
|
|
||||||
Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId);
|
Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId);
|
||||||
_context.Documents.Add(document);
|
_context.Documents.Add(document);
|
||||||
@ -603,7 +510,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
_context.TicketAttachments.Add(attachment);
|
_context.TicketAttachments.Add(attachment);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
string preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
string preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
|
|
||||||
TicketAttachmentVM attachmentVM = attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl);
|
TicketAttachmentVM attachmentVM = attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl);
|
||||||
ticketAttachmentVMs.Add(attachmentVM);
|
ticketAttachmentVMs.Add(attachmentVM);
|
||||||
@ -668,7 +575,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
if (attachment.CommentId == null)
|
if (attachment.CommentId == null)
|
||||||
{
|
{
|
||||||
@ -743,7 +650,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
if (attachment.CommentId == null)
|
if (attachment.CommentId == null)
|
||||||
{
|
{
|
||||||
@ -842,7 +749,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
string preSignedUrl = string.Empty;
|
string preSignedUrl = string.Empty;
|
||||||
if (document != null)
|
if (document != null)
|
||||||
{
|
{
|
||||||
preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(document.S3Key);
|
||||||
}
|
}
|
||||||
if (attachment.CommentId == null)
|
if (attachment.CommentId == null)
|
||||||
{
|
{
|
||||||
|
@ -38,28 +38,9 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(Image.Base64Data))
|
if (string.IsNullOrEmpty(Image.Base64Data))
|
||||||
return BadRequest("Base64 data is missing");
|
return BadRequest("Base64 data is missing");
|
||||||
|
var objectKey = await _s3Service.UploadFileAsync(Image.Base64Data, 1, "Forum");
|
||||||
byte[] fileBytes;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//If base64 has a data URI prefix, strip it
|
|
||||||
var base64 = Image.Base64Data.Contains(",")
|
|
||||||
? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
|
|
||||||
: Image.Base64Data;
|
|
||||||
|
|
||||||
fileBytes = Convert.FromBase64String(base64);
|
|
||||||
}
|
|
||||||
catch (Exception error)
|
|
||||||
{
|
|
||||||
//return BadRequest("Invalid base64 string.");
|
|
||||||
return BadRequest(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var stream = new MemoryStream(fileBytes);
|
|
||||||
var objectKey = await _s3Service.UploadFileAsync(stream, Image.FileName, Image.ContentType);
|
|
||||||
//var objectKey = await _s3Service.UploadFileAsync(Image.FileName, Image.ContentType);
|
//var objectKey = await _s3Service.UploadFileAsync(Image.FileName, Image.ContentType);
|
||||||
var preSignedUrl = await _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
var preSignedUrl = _s3Service.GeneratePreSignedUrlAsync(objectKey);
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Mime-Detective" Version="24.12.2" />
|
||||||
|
<PackageReference Include="Mime-Detective.Definitions.Exhaustive" Version="24.12.2" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
|
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
|
||||||
|
@ -4,6 +4,7 @@ using Amazon.S3.Transfer;
|
|||||||
using Marco.Pms.Model.Utilities;
|
using Marco.Pms.Model.Utilities;
|
||||||
using MarcoBMS.Services.Service;
|
using MarcoBMS.Services.Service;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using MimeDetective;
|
||||||
|
|
||||||
namespace Marco.Pms.Services.Service
|
namespace Marco.Pms.Services.Service
|
||||||
{
|
{
|
||||||
@ -13,10 +14,12 @@ namespace Marco.Pms.Services.Service
|
|||||||
private readonly IAmazonS3 _s3Client;
|
private readonly IAmazonS3 _s3Client;
|
||||||
private readonly string _bucketName = "your-bucket-name";
|
private readonly string _bucketName = "your-bucket-name";
|
||||||
private readonly ILoggingService _logger;
|
private readonly ILoggingService _logger;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
public S3UploadService(IOptions<AWSSettings> awsOptions, ILoggingService logger)
|
public S3UploadService(IOptions<AWSSettings> awsOptions, ILoggingService logger, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_configuration = configuration;
|
||||||
var settings = awsOptions.Value;
|
var settings = awsOptions.Value;
|
||||||
|
|
||||||
var region = Amazon.RegionEndpoint.GetBySystemName(settings.Region);
|
var region = Amazon.RegionEndpoint.GetBySystemName(settings.Region);
|
||||||
@ -25,35 +28,57 @@ namespace Marco.Pms.Services.Service
|
|||||||
_s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region);
|
_s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region);
|
||||||
}
|
}
|
||||||
//public async Task<string> UploadFileAsync(string fileName, string contentType)
|
//public async Task<string> UploadFileAsync(string fileName, string contentType)
|
||||||
public async Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType)
|
public async Task<string> UploadFileAsync(string base64Data, int tenantId, string tag)
|
||||||
{
|
{
|
||||||
// Generate a unique object key (you can customize this)
|
byte[] fileBytes;
|
||||||
var objectKey = $"{fileName}";
|
|
||||||
|
|
||||||
var uploadRequest = new TransferUtilityUploadRequest
|
//If base64 has a data URI prefix, strip it
|
||||||
|
var base64 = base64Data.Contains(",")
|
||||||
|
? base64Data.Substring(base64Data.IndexOf(",") + 1)
|
||||||
|
: base64Data;
|
||||||
|
var allowedFilesType = _configuration.GetSection("WhiteList:ContentType")
|
||||||
|
.GetChildren()
|
||||||
|
.Select(x => x.Value)
|
||||||
|
.ToList();
|
||||||
|
string fileType = GetContentTypeFromBase64(base64);
|
||||||
|
if (allowedFilesType != null && allowedFilesType.Contains(fileType))
|
||||||
{
|
{
|
||||||
InputStream = fileStream,
|
string fileName = GenerateFileName(fileType, tenantId, tag);
|
||||||
Key = objectKey,
|
|
||||||
BucketName = _bucketName,
|
fileBytes = Convert.FromBase64String(base64);
|
||||||
ContentType = contentType,
|
|
||||||
AutoCloseStream = true
|
|
||||||
};
|
using var fileStream = new MemoryStream(fileBytes);
|
||||||
try
|
|
||||||
{
|
// Generate a unique object key (you can customize this)
|
||||||
var transferUtility = new TransferUtility(_s3Client);
|
var objectKey = $"{fileName}";
|
||||||
await transferUtility.UploadAsync(uploadRequest);
|
|
||||||
_logger.LogInfo("File uploaded to Amazon S3");
|
var uploadRequest = new TransferUtilityUploadRequest
|
||||||
return objectKey;
|
{
|
||||||
}
|
InputStream = fileStream,
|
||||||
catch (Exception ex)
|
Key = objectKey,
|
||||||
{
|
BucketName = _bucketName,
|
||||||
_logger.LogError("{error} while uploading file to S3", ex.Message);
|
ContentType = fileType,
|
||||||
return string.Empty;
|
AutoCloseStream = true
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var transferUtility = new TransferUtility(_s3Client);
|
||||||
|
await transferUtility.UploadAsync(uploadRequest);
|
||||||
|
_logger.LogInfo("File uploaded to Amazon S3");
|
||||||
|
return objectKey;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError("{error} while uploading file to S3", ex.Message);
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
throw new InvalidOperationException("Unsupported file type.");
|
||||||
}
|
}
|
||||||
public async Task<string> GeneratePreSignedUrlAsync(string objectKey)
|
public string GeneratePreSignedUrlAsync(string objectKey)
|
||||||
{
|
{
|
||||||
int expiresInMinutes = 1;
|
int expiresInMinutes = 10;
|
||||||
var request = new GetPreSignedUrlRequest
|
var request = new GetPreSignedUrlRequest
|
||||||
{
|
{
|
||||||
BucketName = _bucketName,
|
BucketName = _bucketName,
|
||||||
@ -95,11 +120,116 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
public string GenerateFileName(string contentType, int tenantId, string? name)
|
public string GenerateFileName(string contentType, int tenantId, string? name)
|
||||||
{
|
{
|
||||||
string extenstion = contentType.Split("/")[1];
|
string extenstion = GetExtensionFromMimeType(contentType);
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
return $"{tenantId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}.{extenstion}";
|
|
||||||
return $"{name}_{tenantId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}.{extenstion}";
|
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return $"{tenantId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}{extenstion}";
|
||||||
|
return $"{name}_{tenantId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}{extenstion}";
|
||||||
|
|
||||||
|
}
|
||||||
|
public string GetExtensionFromMimeType(string contentType)
|
||||||
|
{
|
||||||
|
switch (contentType.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "application/pdf":
|
||||||
|
return ".pdf";
|
||||||
|
|
||||||
|
case "application/msword":
|
||||||
|
return ".doc";
|
||||||
|
|
||||||
|
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||||
|
return ".docx";
|
||||||
|
|
||||||
|
case "application/vnd.ms-excel":
|
||||||
|
return ".xls";
|
||||||
|
|
||||||
|
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
||||||
|
return ".xlsx";
|
||||||
|
|
||||||
|
case "application/mspowerpoint":
|
||||||
|
return ".ppt";
|
||||||
|
|
||||||
|
case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
||||||
|
return ".pptx";
|
||||||
|
|
||||||
|
case "text/plain":
|
||||||
|
return ".txt";
|
||||||
|
|
||||||
|
case "application/rtf":
|
||||||
|
return ".rtf";
|
||||||
|
|
||||||
|
case "image/jpeg":
|
||||||
|
return ".jpg";
|
||||||
|
|
||||||
|
case "image/png":
|
||||||
|
return ".png";
|
||||||
|
|
||||||
|
case "image/gif":
|
||||||
|
return ".gif";
|
||||||
|
|
||||||
|
case "image/bmp":
|
||||||
|
return ".bmp";
|
||||||
|
|
||||||
|
case "text/csv":
|
||||||
|
return ".csv";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""; // or ".bin", or throw an error, based on your needs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string GetContentTypeFromBase64(string base64String)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(base64String))
|
||||||
|
{
|
||||||
|
return string.Empty; // Or handle the empty case as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 1. Decode the Base64 string to a byte array
|
||||||
|
byte[] decodedBytes = Convert.FromBase64String(base64String);
|
||||||
|
|
||||||
|
// 2. Create a ContentInspector (using default definitions for this example)
|
||||||
|
var inspector = new ContentInspectorBuilder()
|
||||||
|
{
|
||||||
|
Definitions = MimeDetective.Definitions.DefaultDefinitions.All()
|
||||||
|
}.Build();
|
||||||
|
|
||||||
|
// 3. Inspect the byte array to determine the content type
|
||||||
|
var results = inspector.Inspect(decodedBytes);
|
||||||
|
|
||||||
|
if (results.Any())
|
||||||
|
{
|
||||||
|
var bestMatch = results
|
||||||
|
.OrderByDescending(r => r.Points)
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (bestMatch?.Definition?.File?.MimeType != null)
|
||||||
|
{
|
||||||
|
return bestMatch.Definition.File.MimeType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Warning: Could not find MimeType, Type, or ContentType property in Definition.");
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "application/octet-stream"; // Default if type cannot be determined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
// Handle cases where the input string is not valid Base64
|
||||||
|
Console.WriteLine("Error: Invalid Base64 string.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Handle other potential errors during decoding or inspection
|
||||||
|
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -86,6 +86,24 @@
|
|||||||
"SecretKey": "NTS5XXgZINQbU6ctpNuLXtIY/Qk9GCgD9Rr5yNJP",
|
"SecretKey": "NTS5XXgZINQbU6ctpNuLXtIY/Qk9GCgD9Rr5yNJP",
|
||||||
"Region": "us-east-1",
|
"Region": "us-east-1",
|
||||||
"BucketName": "testenv-marco-pms-documents"
|
"BucketName": "testenv-marco-pms-documents"
|
||||||
|
},
|
||||||
|
"WhiteList": {
|
||||||
|
"ContentType": [
|
||||||
|
"application/pdf", // pdf
|
||||||
|
"application/msword", // Doc
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //docx
|
||||||
|
"application/vnd.ms-excel", //xls
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //xlsx
|
||||||
|
"application/mspowerpoint", //ppt
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //pptx
|
||||||
|
"text/plain", //txt
|
||||||
|
"application/rtf", //rtf
|
||||||
|
"text/csv", //csv
|
||||||
|
"image/jpg", //jpg
|
||||||
|
"image/jpeg", //jpeg
|
||||||
|
"image/png", //png
|
||||||
|
"image/gif", //gif
|
||||||
|
"image/bmp" //bmp
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user