diff options
| author | ivar <i@oiee.no> | 2025-10-20 00:26:34 +0200 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2025-10-20 00:26:34 +0200 |
| commit | a1f0518d0cd123a791adde64f4f11bd8e44276c7 (patch) | |
| tree | 675a7dff8262eea877ec800ff1efe9b92f5d7e7d /api/WhatApi/Endpoints | |
| download | what-a1f0518d0cd123a791adde64f4f11bd8e44276c7.tar.xz what-a1f0518d0cd123a791adde64f4f11bd8e44276c7.zip | |
Initial commit
Diffstat (limited to 'api/WhatApi/Endpoints')
| -rw-r--r-- | api/WhatApi/Endpoints/BaseEndpoint.cs | 12 | ||||
| -rw-r--r-- | api/WhatApi/Endpoints/DownloadContentEndpoint.cs | 19 | ||||
| -rw-r--r-- | api/WhatApi/Endpoints/GetPlacesEndpoint.cs | 57 | ||||
| -rw-r--r-- | api/WhatApi/Endpoints/UploadContentEndpoint.cs | 49 |
4 files changed, 137 insertions, 0 deletions
diff --git a/api/WhatApi/Endpoints/BaseEndpoint.cs b/api/WhatApi/Endpoints/BaseEndpoint.cs new file mode 100644 index 0000000..4d8f9ad --- /dev/null +++ b/api/WhatApi/Endpoints/BaseEndpoint.cs @@ -0,0 +1,12 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; + +namespace WhatApi.Endpoints; + +[ApiController] +public class BaseEndpoint : ControllerBase +{ + protected IPAddress GetIp() => Request.Headers.TryGetValue("X-Forwarded-For", out var ip) + ? IPAddress.Parse(ip.ToString()) + : HttpContext.Connection.RemoteIpAddress!; +}
\ No newline at end of file diff --git a/api/WhatApi/Endpoints/DownloadContentEndpoint.cs b/api/WhatApi/Endpoints/DownloadContentEndpoint.cs new file mode 100644 index 0000000..dbe6bff --- /dev/null +++ b/api/WhatApi/Endpoints/DownloadContentEndpoint.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WhatApi.Endpoints; + +public class DownloadContentEndpoint : BaseEndpoint +{ + [HttpGet("~/{id:guid}")] + public async Task<ActionResult> HandleAsync(Guid id, CancellationToken ct = default) { + try { + var path = Path.Combine(Directory.GetCurrentDirectory(), "files", id.ToString()); + await using var file = new FileStream(path, FileMode.Open); + if (!file.CanRead) return NotFound(); + return File(file, "application/octet-stream", id.ToString()); + } catch (Exception e) { + if (e is not FileNotFoundException) Console.WriteLine(e); + return NotFound(); + } + } +}
\ No newline at end of file diff --git a/api/WhatApi/Endpoints/GetPlacesEndpoint.cs b/api/WhatApi/Endpoints/GetPlacesEndpoint.cs new file mode 100644 index 0000000..5630229 --- /dev/null +++ b/api/WhatApi/Endpoints/GetPlacesEndpoint.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using NetTopologySuite; +using NetTopologySuite.Features; +using NetTopologySuite.Geometries; +using WhatApi.Tables; + +namespace WhatApi.Endpoints; + +public class GetPlacesEndpoint(Database db) : BaseEndpoint +{ + [HttpGet("~/places")] + public async Task<ActionResult> HandleAsync(string w, string s, string e, string n, CancellationToken ct = default) { + var north = double.Parse(n); + var east = double.Parse(e); + var south = double.Parse(s); + var west = double.Parse(w); + + IQueryable<Place> resultingQuery; + + if (west > east) { + resultingQuery = db.Places + .FromSqlInterpolated($""" + SELECT * FROM "Place" + WHERE ST_Intersects( + "Location", + ST_MakeEnvelope({west}, {south}, 180, {north}, {Constants.Wgs84SpatialReferenceId}) || ST_MakeEnvelope(-180, {south}, {east}, {north}, {Constants.Wgs84SpatialReferenceId}) + ) + """); + } else { + resultingQuery = db.Places + .FromSqlInterpolated($""" + SELECT * FROM "Place" + WHERE ST_Intersects( + "Location", + ST_MakeEnvelope({west}, {south}, {east}, {north}, {Constants.Wgs84SpatialReferenceId}) + ) + """); + } + + var gf = NtsGeometryServices.Instance.CreateGeometryFactory(srid: Constants.Wgs84SpatialReferenceId); + var fc = new FeatureCollection(); + + await foreach (var p in resultingQuery.AsAsyncEnumerable().WithCancellation(ct)) { + var point = gf.CreatePoint(new Coordinate(p.Location.X, p.Location.Y)); + fc.Add(new Feature(point, new AttributesTable { + { + "id", p.Id + }, { + "cid", p.ContentId + } + })); + } + + return Ok(fc); + } +}
\ No newline at end of file diff --git a/api/WhatApi/Endpoints/UploadContentEndpoint.cs b/api/WhatApi/Endpoints/UploadContentEndpoint.cs new file mode 100644 index 0000000..82fb71b --- /dev/null +++ b/api/WhatApi/Endpoints/UploadContentEndpoint.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using NetTopologySuite; +using NetTopologySuite.Geometries; + +namespace WhatApi.Endpoints; + +public class UploadContentEndpoint(Database db) : BaseEndpoint +{ + public record UploadContent(IFormFile File, string LatLong); + + [HttpPost("~/upload")] + public async Task<ActionResult> HandleAsync([FromForm] UploadContent request, CancellationToken ct = default) { + if (string.IsNullOrWhiteSpace(Request.GetMultipartBoundary())) { + return StatusCode(415, "Unsupported Media Type"); + } + + var blobId = Guid.NewGuid(); + var contentId = Guid.NewGuid(); + + var latitude = request.LatLong.Split(',')[0]; + var longitude = request.LatLong.Split(',')[1]; + + var gf = NtsGeometryServices.Instance.CreateGeometryFactory(srid: Constants.Wgs84SpatialReferenceId); + var point = gf.CreatePoint(new Coordinate(double.Parse(longitude), double.Parse(latitude))); + + var place = new Tables.Place() { + ContentId = contentId, + Location = point + }; + + var content = new Tables.Content() { + Id = contentId, + Mime = request.File.ContentType, + Created = DateTime.UtcNow, + BlobId = blobId, + Ip = GetIp() + }; + + var path = Path.Combine(Directory.GetCurrentDirectory(), "files", blobId.ToString()); + + await using var writer = new FileStream(path, FileMode.CreateNew); + await request.File.CopyToAsync(writer, ct); + await db.Content.AddAsync(content, ct); + await db.Places.AddAsync(place, ct); + await db.SaveChangesAsync(ct); + return Ok(contentId); + } +}
\ No newline at end of file |
