From 9ab36c2bdea8594df9726a69000fc84be7e31538 Mon Sep 17 00:00:00 2001 From: TuTiDore Date: Sun, 21 Dec 2025 13:38:26 -0800 Subject: [PATCH 1/3] feat: add v2 endpoint /shockers/logs --- API/Controller/Shockers/GetShockerLogs.cs | 61 ++++++++++++++++++++++- API/Models/Response/LogEntryV2.cs | 29 +++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 API/Models/Response/LogEntryV2.cs diff --git a/API/Controller/Shockers/GetShockerLogs.cs b/API/Controller/Shockers/GetShockerLogs.cs index a490ddda..72fb57b8 100644 --- a/API/Controller/Shockers/GetShockerLogs.cs +++ b/API/Controller/Shockers/GetShockerLogs.cs @@ -28,7 +28,7 @@ public sealed partial class ShockerController [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] // ShockerNotFound [MapToApiVersion("1")] - public async Task GetShockerLogs([FromRoute] Guid shockerId, [FromQuery(Name="offset")] uint offset = 0, + public async Task GetShockerLogs([FromRoute] Guid shockerId, [FromQuery(Name = "offset")] uint offset = 0, [FromQuery, Range(1, 500)] uint limit = 100) { var exists = await _db.Shockers.AnyAsync(x => x.Device.OwnerId == CurrentUser.Id && x.Id == shockerId); @@ -66,4 +66,63 @@ public async Task GetShockerLogs([FromRoute] Guid shockerId, [Fro return LegacyDataOk(logs); } + + + /// + /// Get the logs for all shockers + /// + /// + /// + /// The logs + /// Shocker does not exist + [HttpGet("logs")] + [EnableRateLimiting("shocker-logs")] + [ProducesResponseType(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] + [MapToApiVersion("2")] + public async Task GetAllShockerLogs([FromQuery(Name = "offset")] uint offset = 0, + [FromQuery, Range(1, 500)] uint limit = 100) + { + var logs = await _db.ShockerControlLogs + .Include(x => x.Shocker) + .ThenInclude(x => x.Device) + .Where(x => x.Shocker.Device.OwnerId == CurrentUser.Id) + .OrderByDescending(x => x.CreatedAt) + .Skip((int)offset) + .Take((int)limit) + .Select(x => new LogEntryV2 + { + Id = x.Id, + DeviceId = x.Shocker.Device.Id, + DeviceName = x.Shocker.Device.Name, + ShockerId = x.Shocker.Id, + ShockerName = x.Shocker.Name, + Duration = x.Duration, + Intensity = x.Intensity, + Type = x.Type, + CreatedOn = x.CreatedAt, + ControlledBy = x.ControlledByUser == null + ? new ControlLogSenderLight + { + Id = Guid.Empty, + Name = "Guest", + Image = GravatarUtils.GuestImageUrl, + CustomName = x.CustomName + } + : new ControlLogSenderLight + { + Id = x.ControlledByUser.Id, + Name = x.ControlledByUser.Name, + Image = x.ControlledByUser.GetImageUrl(), + CustomName = x.CustomName + } + }) + .ToListAsync(); + + return new ShockerLogsResponse + { + Logs = logs + }; + } + } \ No newline at end of file diff --git a/API/Models/Response/LogEntryV2.cs b/API/Models/Response/LogEntryV2.cs new file mode 100644 index 00000000..8a01ef37 --- /dev/null +++ b/API/Models/Response/LogEntryV2.cs @@ -0,0 +1,29 @@ +using OpenShock.Common.Models; + +namespace OpenShock.API.Models.Response; + +public sealed class ShockerLogsResponse +{ + public required ICollection Logs { get; init; } +} + +public sealed class LogEntryV2 +{ + public required Guid Id { get; init; } + + public required Guid DeviceId { get; init; } + public required string DeviceName { get; init; } + + public required Guid ShockerId { get; init; } + public required string ShockerName { get; init; } + + public required DateTime CreatedOn { get; init; } + + public required ControlType Type { get; init; } + + public required ControlLogSenderLight ControlledBy { get; init; } + + public required byte Intensity { get; init; } + + public required uint Duration { get; init; } +} \ No newline at end of file From ddb325b8158cbda352ce7ec5b1fda53c1efd4d80 Mon Sep 17 00:00:00 2001 From: TuTiDore Date: Sun, 21 Dec 2025 14:02:09 -0800 Subject: [PATCH 2/3] fix: use v1 api, "Hub" --- API/Controller/Shockers/GetShockerLogs.cs | 23 ++++++++----------- .../{LogEntryV2.cs => LogEntryWithHub.cs} | 11 +++------ 2 files changed, 12 insertions(+), 22 deletions(-) rename API/Models/Response/{LogEntryV2.cs => LogEntryWithHub.cs} (67%) diff --git a/API/Controller/Shockers/GetShockerLogs.cs b/API/Controller/Shockers/GetShockerLogs.cs index 72fb57b8..6477bdd3 100644 --- a/API/Controller/Shockers/GetShockerLogs.cs +++ b/API/Controller/Shockers/GetShockerLogs.cs @@ -77,24 +77,22 @@ public async Task GetShockerLogs([FromRoute] Guid shockerId, [Fro /// Shocker does not exist [HttpGet("logs")] [EnableRateLimiting("shocker-logs")] - [ProducesResponseType(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] + [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] - [MapToApiVersion("2")] - public async Task GetAllShockerLogs([FromQuery(Name = "offset")] uint offset = 0, + [MapToApiVersion("1")] + public IActionResult GetAllShockerLogs([FromQuery(Name = "offset")] uint offset = 0, [FromQuery, Range(1, 500)] uint limit = 100) { - var logs = await _db.ShockerControlLogs - .Include(x => x.Shocker) - .ThenInclude(x => x.Device) + var logs = _db.ShockerControlLogs .Where(x => x.Shocker.Device.OwnerId == CurrentUser.Id) .OrderByDescending(x => x.CreatedAt) .Skip((int)offset) .Take((int)limit) - .Select(x => new LogEntryV2 + .Select(x => new LogEntryWithHub { Id = x.Id, - DeviceId = x.Shocker.Device.Id, - DeviceName = x.Shocker.Device.Name, + HubId = x.Shocker.Device.Id, + HubName = x.Shocker.Device.Name, ShockerId = x.Shocker.Id, ShockerName = x.Shocker.Name, Duration = x.Duration, @@ -117,12 +115,9 @@ public async Task GetAllShockerLogs([FromQuery(Name = "offs CustomName = x.CustomName } }) - .ToListAsync(); + .AsAsyncEnumerable(); - return new ShockerLogsResponse - { - Logs = logs - }; + return LegacyDataOk(logs); } } \ No newline at end of file diff --git a/API/Models/Response/LogEntryV2.cs b/API/Models/Response/LogEntryWithHub.cs similarity index 67% rename from API/Models/Response/LogEntryV2.cs rename to API/Models/Response/LogEntryWithHub.cs index 8a01ef37..d429b73b 100644 --- a/API/Models/Response/LogEntryV2.cs +++ b/API/Models/Response/LogEntryWithHub.cs @@ -2,17 +2,12 @@ namespace OpenShock.API.Models.Response; -public sealed class ShockerLogsResponse -{ - public required ICollection Logs { get; init; } -} - -public sealed class LogEntryV2 +public sealed class LogEntryWithHub { public required Guid Id { get; init; } - public required Guid DeviceId { get; init; } - public required string DeviceName { get; init; } + public required Guid HubId { get; init; } + public required string HubName { get; init; } public required Guid ShockerId { get; init; } public required string ShockerName { get; init; } From 84eba1c62c95bff0f5f7c6992cce954e54fd52b3 Mon Sep 17 00:00:00 2001 From: TuTiDore Date: Sun, 21 Dec 2025 14:47:18 -0800 Subject: [PATCH 3/3] fix: use ShockerLogsResponse --- API/Controller/Shockers/GetShockerLogs.cs | 14 ++++++++------ API/Models/Response/LogEntryWithHub.cs | 5 +++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/API/Controller/Shockers/GetShockerLogs.cs b/API/Controller/Shockers/GetShockerLogs.cs index 6477bdd3..c4a246fa 100644 --- a/API/Controller/Shockers/GetShockerLogs.cs +++ b/API/Controller/Shockers/GetShockerLogs.cs @@ -77,13 +77,13 @@ public async Task GetShockerLogs([FromRoute] Guid shockerId, [Fro /// Shocker does not exist [HttpGet("logs")] [EnableRateLimiting("shocker-logs")] - [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] [MapToApiVersion("1")] - public IActionResult GetAllShockerLogs([FromQuery(Name = "offset")] uint offset = 0, + public async Task GetAllShockerLogs([FromQuery(Name = "offset")] uint offset = 0, [FromQuery, Range(1, 500)] uint limit = 100) { - var logs = _db.ShockerControlLogs + var logs = await _db.ShockerControlLogs .Where(x => x.Shocker.Device.OwnerId == CurrentUser.Id) .OrderByDescending(x => x.CreatedAt) .Skip((int)offset) @@ -115,9 +115,11 @@ public IActionResult GetAllShockerLogs([FromQuery(Name = "offset")] uint offset CustomName = x.CustomName } }) - .AsAsyncEnumerable(); + .ToListAsync(); - return LegacyDataOk(logs); + return Ok(new ShockerLogsResponse + { + Logs = logs + }); } - } \ No newline at end of file diff --git a/API/Models/Response/LogEntryWithHub.cs b/API/Models/Response/LogEntryWithHub.cs index d429b73b..4eb22469 100644 --- a/API/Models/Response/LogEntryWithHub.cs +++ b/API/Models/Response/LogEntryWithHub.cs @@ -2,6 +2,11 @@ namespace OpenShock.API.Models.Response; +public sealed class ShockerLogsResponse +{ + public required ICollection Logs { get; init; } +} + public sealed class LogEntryWithHub { public required Guid Id { get; init; }