feat: implement stage 2 of Milkdown integration (secure upload & xss guard)
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using NexusReader.Application.Abstractions.Services;
|
||||
|
||||
namespace NexusReader.Infrastructure.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Infrastructure implementation of general storage utilizing local filesystem.
|
||||
/// Files are saved in wwwroot/uploads/media.
|
||||
/// </summary>
|
||||
public class LocalStorageService : IStorageService
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public LocalStorageService(IWebHostEnvironment environment)
|
||||
{
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
public async Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string contentType)
|
||||
{
|
||||
using var stream = new MemoryStream(fileBytes);
|
||||
return await UploadFileAsync(stream, fileName, contentType);
|
||||
}
|
||||
|
||||
public async Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
var mediaFolder = Path.Combine(_environment.WebRootPath, "uploads", "media");
|
||||
|
||||
if (!Directory.Exists(mediaFolder))
|
||||
{
|
||||
Directory.CreateDirectory(mediaFolder);
|
||||
}
|
||||
|
||||
// Clean file name to prevent path traversal issues
|
||||
var safeFileName = Path.GetFileName(fileName);
|
||||
var uniqueFileName = $"{Guid.NewGuid()}_{safeFileName}";
|
||||
var filePath = Path.Combine(mediaFolder, uniqueFileName);
|
||||
|
||||
using (var outputStream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await fileStream.CopyToAsync(outputStream);
|
||||
}
|
||||
|
||||
// Return the public web-relative URL
|
||||
return $"/uploads/media/{uniqueFileName}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user