From f60f6139692aad08c9cf00d3d66afba5f9dfa3f7 Mon Sep 17 00:00:00 2001 From: eee4 <41441600+eee4@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:19:29 +0200 Subject: [PATCH] feat: template for image upload --- Controllers/UserContentController.cs | 119 +++++++++++++++++++++++++++ QuotifyBE.csproj | 4 + appsettings.example.json | 5 +- 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 Controllers/UserContentController.cs diff --git a/Controllers/UserContentController.cs b/Controllers/UserContentController.cs new file mode 100644 index 0000000..413a4af --- /dev/null +++ b/Controllers/UserContentController.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using QuotifyBE.Data; +using QuotifyBE.Entities; +using QuotifyBE.DTOs; +using QuotifyBE.Mapping; +using Microsoft.AspNetCore.Cors; +using Microsoft.EntityFrameworkCore; + +namespace QuotifyBE.Controllers; + + +[ApiController] +[EnableCors] +[Route("api/v1/uc")] +[Produces("application/json")] +public class UserContentController : ControllerBase +{ + + private readonly IConfiguration _appsettings; + private readonly ApplicationDbContext _db; + private readonly GeneralUseHelpers guhf; + + public UserContentController(IConfiguration appsettings, ApplicationDbContext db, GeneralUseHelpers GUHF) + { + _appsettings = appsettings; + _db = db; + guhf = GUHF; + } + + // GET /api/v1/uc/images + /// + /// [AUTHED] Get every image + /// + /// + /// Can (and will) return an empty list if no images are found in DB.
+ /// Requires authorization with a JWT, has CORS set. + ///
+ /// Returned on valid request + [HttpGet] + [Authorize] + [EnableCors] + [ProducesResponseType(typeof(List), 200)] + public async Task GetImages() + { + + // Get all the images + List images = await _db.Images + .ToListAsync(); + + // Return to user + return Ok(images); + + } + + + // POST /api/v1/uc/images + /// + /// [AUTHED] Upload an image and get an its URI + /// + /// + /// Allows authorized users to publish images. + /// A user-reachable path is returned on success.
+ ///
+ /// Returned on valid request + /// Returned when file extension is unknown + /// Returned when request does not follow user-provided config + [HttpPost] + [Authorize] + [EnableCors] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ErrorDTO), 400)] + [ProducesResponseType(typeof(ErrorDTO), 406)] + public IActionResult PostNewImage(IFormFile file) + { + + // Ideally, a hash of the file would be stored somewhere + // in the database to have a basic redundancy check, + // but this will do for now. ~eee4 + + // A good idea would be to also check the Content-Type + // of submitted files. ~eee4 + + List allowedExtensions = new List() { ".jpg", ".png", ".jfif", ".gif", ".avif", ".webp" }; + + string fileExtension = Path.GetExtension(file.FileName); + if (!allowedExtensions.Contains(fileExtension.ToLower())) { + return BadRequest(new ErrorDTO { + Status = "error", + Error_msg = $"Unknown file extension. Please use one of the following: {string.Join(", ", allowedExtensions)}" + }); + } + + // TODO: + // https://www.youtube.com/watch?v=6-FNejMrVuk + + // Sprawdź, czy plik spełnia ograniczenia: + // 1. Czy rozmiar jest mniejszy od _appsettings["UserContent"]["MaxFileSize"] ? + + + // Jeśli nie, zwróć ErrorDTO ze wiadomością: $"File size exceeds {_appsettings["UserContent"]["MaxFileSize"]}" + + + // Zapisz plik na dysku z pseudolosową nazwą GUID + + + // Wrzucić go do folderu "uploads/images/" + + + // Stwórz URL postaci: "/uploads/images/." + + + // Zwróć powyższy URL + return Ok(new { Status = "ok", Filepath = "miejsce na wspomniany URL" }); + + } + +} + diff --git a/QuotifyBE.csproj b/QuotifyBE.csproj index 4d09be2..de69528 100644 --- a/QuotifyBE.csproj +++ b/QuotifyBE.csproj @@ -35,4 +35,8 @@ + + + + diff --git a/appsettings.example.json b/appsettings.example.json index 000ae2f..e2a219a 100644 --- a/appsettings.example.json +++ b/appsettings.example.json @@ -2,8 +2,11 @@ "JwtSecret": "this is a sample jwt secret token required for quotify - it needs to have at least 256 bits (32 bytes long)", "DomainName": "example.com", "CorsOrigins": [ - "http://localhost:5259", "http://localhost:5258", "http://example.com" + "http://localhost:5259", "http://localhost:5258", "http://localhost:3000", "http://example.com" ], + "UserContent": { + "MaxFileSize": 5242880, + }, "ConnectionStrings": { "DefaultConnection": "Server=server-host;Database=db-name;Username=quotify-user;Password=user-secret" },