From 521c95c10b871b7b0723c140c003264bf18df966 Mon Sep 17 00:00:00 2001 From: Fritiof Hedman Date: Sun, 7 Jun 2026 14:47:41 +0200 Subject: [PATCH] 5.2 ImageController.GetImage Co-Authored-By: Claude Opus 4.7 (1M context) --- IMPLEMENTATION.md | 2 +- .../Controllers/ImageController.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/FrameProcessor/Controllers/ImageController.cs diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index fbab822..a83ed20 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -136,7 +136,7 @@ Each type lives in `src/FrameProcessor/Domain/`. Tests in `tests/FrameProcessor. - Call `ImagePipeline.Process`, then `ImageStore.WriteAsync`. - Return `200 { frame, mac, url, processedAt, mqttPublished: false }` (MQTT stubbed). -### [ ] 5.2 `ImageController.GetImage` +### [x] 5.2 `ImageController.GetImage` - Route: `GET /i/{mac}.png`. - Normalize `{mac}` via `MacAddress.TryParse` → 404 on bad form. - Look up frame by MAC → 404 if unknown or file absent. diff --git a/src/FrameProcessor/Controllers/ImageController.cs b/src/FrameProcessor/Controllers/ImageController.cs new file mode 100644 index 0000000..e1a273f --- /dev/null +++ b/src/FrameProcessor/Controllers/ImageController.cs @@ -0,0 +1,40 @@ +using FrameProcessor.Configuration; +using FrameProcessor.Domain; +using FrameProcessor.Storage; +using Microsoft.AspNetCore.Mvc; + +namespace FrameProcessor.Controllers; + +[ApiController] +public sealed class ImageController : ControllerBase +{ + private readonly FramesRegistry _frames; + private readonly ImageStore _store; + + public ImageController(FramesRegistry frames, ImageStore store) + { + _frames = frames; + _store = store; + } + + [HttpGet("/i/{mac}.png")] + public IActionResult GetImage(string mac) + { + if (!MacAddress.TryParse(mac, out var macAddress) || !_frames.TryGetByMac(macAddress, out _)) + { + return NotFound(); + } + + if (!_store.TryGetPath(macAddress, out var path)) + { + return NotFound(); + } + + var mtime = System.IO.File.GetLastWriteTimeUtc(path); + Response.Headers.CacheControl = "no-store"; + Response.Headers.ETag = $"\"{mtime.Ticks:x}\""; + + var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + return File(stream, "image/png"); + } +}