Initial commit
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RobotNet10.MapEditor.Shared.DTOs.Responses;
|
||||
using RobotNet10.MapManager.Services;
|
||||
|
||||
namespace RobotNet10.MapManager.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller for managing background images for layout levels
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/images")]
|
||||
[Authorize]
|
||||
public class ImagesController(
|
||||
IImageStorageService imageStorageService,
|
||||
ILogger<ImagesController> logger) : ControllerBase
|
||||
{
|
||||
private readonly IImageStorageService _imageStorageService = imageStorageService;
|
||||
private readonly ILogger<ImagesController> _logger = logger;
|
||||
|
||||
/// <summary>
|
||||
/// Get background image for a layout level
|
||||
/// </summary>
|
||||
/// <param name="layoutLevelId">Layout level ID</param>
|
||||
/// <returns>Image file (PNG)</returns>
|
||||
[HttpGet("layout/{layoutLevelId}")]
|
||||
[ProducesResponseType(typeof(FileStreamResult), 200)]
|
||||
[ProducesResponseType(404)]
|
||||
public async Task<IActionResult> GetLayoutImage(Guid layoutLevelId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var imageStream = await _imageStorageService.GetImageAsync(layoutLevelId);
|
||||
|
||||
if (imageStream == null)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning("Image not found for layout level {LevelId}", layoutLevelId);
|
||||
return NotFound(CreateErrorResponse($"Image not found for layout level '{layoutLevelId}'", "IMAGE_NOT_FOUND"));
|
||||
}
|
||||
|
||||
return File(imageStream, "image/png", $"{layoutLevelId}.png");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error retrieving image for layout level {LevelId}", layoutLevelId);
|
||||
return StatusCode(500, CreateErrorResponse("Internal server error while retrieving image", "INTERNAL_ERROR"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload or replace background image for a layout level
|
||||
/// </summary>
|
||||
/// <param name="layoutLevelId">Layout level ID</param>
|
||||
/// <param name="file">Image file (PNG format)</param>
|
||||
/// <returns>Success message</returns>
|
||||
[HttpPost("layout/{layoutLevelId}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(400)]
|
||||
public async Task<IActionResult> UploadLayoutImage(Guid layoutLevelId, IFormFile file)
|
||||
{
|
||||
// Validate file
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return BadRequest(CreateErrorResponse("No file provided", "VALIDATION_ERROR"));
|
||||
}
|
||||
|
||||
// Validate file type
|
||||
if (!file.ContentType.Equals("image/png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return BadRequest(CreateErrorResponse("Only PNG images are supported", "VALIDATION_ERROR"));
|
||||
}
|
||||
|
||||
// Validate file size (max 10MB)
|
||||
const long maxFileSize = 10 * 1024 * 1024; // 10MB
|
||||
if (file.Length > maxFileSize)
|
||||
{
|
||||
return BadRequest(CreateErrorResponse($"File size exceeds maximum of {maxFileSize / 1024 / 1024}MB", "VALIDATION_ERROR"));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = file.OpenReadStream();
|
||||
await _imageStorageService.SaveImageAsync(layoutLevelId, stream);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
message = "Image uploaded successfully",
|
||||
layoutLevelId,
|
||||
fileName = $"{layoutLevelId}.png",
|
||||
size = file.Length
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error uploading image for layout level {LevelId}", layoutLevelId);
|
||||
return StatusCode(500, CreateErrorResponse("Internal server error while uploading image", "INTERNAL_ERROR"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete background image for a layout level
|
||||
/// </summary>
|
||||
/// <param name="layoutLevelId">Layout level ID</param>
|
||||
/// <returns>No content on success</returns>
|
||||
[HttpDelete("layout/{layoutLevelId}")]
|
||||
[ProducesResponseType(204)]
|
||||
[ProducesResponseType(404)]
|
||||
public async Task<IActionResult> DeleteLayoutImage(Guid layoutLevelId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deleted = await _imageStorageService.DeleteImageAsync(layoutLevelId);
|
||||
|
||||
if (!deleted)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Warning)) _logger.LogWarning("Image not found for deletion: {LevelId}", layoutLevelId);
|
||||
return NotFound(CreateErrorResponse($"Image not found for layout level '{layoutLevelId}'", "IMAGE_NOT_FOUND"));
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Error)) _logger.LogError(ex, "Error deleting image for layout level {LevelId}", layoutLevelId);
|
||||
return StatusCode(500, CreateErrorResponse("Internal server error while deleting image", "INTERNAL_ERROR"));
|
||||
}
|
||||
}
|
||||
|
||||
private static ErrorResponseDto CreateErrorResponse(
|
||||
string error,
|
||||
string? errorCode = null,
|
||||
Dictionary<string, object>? details = null)
|
||||
{
|
||||
return new ErrorResponseDto
|
||||
{
|
||||
Error = error,
|
||||
ErrorCode = errorCode,
|
||||
Details = details
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user