Skip to content

Commit 8be6ca2

Browse files
committed
Merge branch 'master' of https://github.com/MachDatum/ThingConnect.Pulse into apply-result
2 parents ceb48fa + ae022fc commit 8be6ca2

File tree

55 files changed

+2562
-1174
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2562
-1174
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"Bash(timeout:*)",
1414
"Bash(ping:*)",
1515
"Bash(findstr:*)",
16-
"Bash(npx eslint:*)"
16+
"Bash(npx eslint:*)",
17+
"Read(//c/ProgramData/ThingConnect.Pulse/logs/**)"
1718
],
1819
"deny": [],
1920
"ask": []

ThingConnect.Pulse.Server/Controllers/AuthController.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ public AuthController(
2929
}
3030

3131
/// <summary>
32-
/// User login - creates authentication cookie
32+
/// User login - creates authentication cookie.
3333
/// </summary>
34+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
3435
[HttpPost("login")]
3536
public async Task<ActionResult<UserInfoDto>> LoginAsync([FromBody] LoginRequestDto request)
3637
{
@@ -88,8 +89,9 @@ public async Task<ActionResult<UserInfoDto>> LoginAsync([FromBody] LoginRequestD
8889
}
8990

9091
/// <summary>
91-
/// Initial user registration - only allows admin creation if no users exist
92+
/// Initial user registration - only allows admin creation if no users exist.
9293
/// </summary>
94+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
9395
[HttpPost("register")]
9496
public async Task<ActionResult<UserInfoDto>> RegisterAsync([FromBody] RegisterRequestDto request)
9597
{
@@ -124,7 +126,7 @@ public async Task<ActionResult<UserInfoDto>> RegisterAsync([FromBody] RegisterRe
124126

125127
// Sign in the user immediately after successful registration
126128
await _signInManager.SignInAsync(user, isPersistent: true);
127-
129+
128130
// Update last login time since we just signed them in
129131
user.LastLoginAt = DateTimeOffset.UtcNow;
130132
await _userManager.UpdateAsync(user);
@@ -150,8 +152,9 @@ public async Task<ActionResult<UserInfoDto>> RegisterAsync([FromBody] RegisterRe
150152
}
151153

152154
/// <summary>
153-
/// Check if initial setup is needed
155+
/// Check if initial setup is needed.
154156
/// </summary>
157+
/// <returns></returns>
155158
[HttpGet("setup-required")]
156159
public ActionResult<bool> IsSetupRequired()
157160
{
@@ -168,8 +171,9 @@ public ActionResult<bool> IsSetupRequired()
168171
}
169172

170173
/// <summary>
171-
/// Get current user session information
174+
/// Get current user session information.
172175
/// </summary>
176+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
173177
[HttpGet("session")]
174178
[Authorize]
175179
public async Task<ActionResult<UserInfoDto>> GetSessionAsync()
@@ -201,8 +205,9 @@ public async Task<ActionResult<UserInfoDto>> GetSessionAsync()
201205
}
202206

203207
/// <summary>
204-
/// Change user's own password
208+
/// Change user's own password.
205209
/// </summary>
210+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
206211
[HttpPost("change-password")]
207212
[Authorize]
208213
public async Task<IActionResult> ChangePasswordAsync([FromBody] ChangePasswordDto request)
@@ -237,8 +242,9 @@ public async Task<IActionResult> ChangePasswordAsync([FromBody] ChangePasswordDt
237242
}
238243

239244
/// <summary>
240-
/// Logout - removes authentication cookie
245+
/// Logout - removes authentication cookie.
241246
/// </summary>
247+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
242248
[HttpPost("logout")]
243249
[Authorize]
244250
public async Task<IActionResult> LogoutAsync()
@@ -263,8 +269,9 @@ public async Task<IActionResult> LogoutAsync()
263269
}
264270

265271
/// <summary>
266-
/// Save telemetry consent settings during onboarding
272+
/// Save telemetry consent settings during onboarding.
267273
/// </summary>
274+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
268275
[HttpPost("telemetry-consent")]
269276
public async Task<IActionResult> SaveTelemetryConsentAsync([FromBody] TelemetryConsentDto request)
270277
{
@@ -288,8 +295,9 @@ public async Task<IActionResult> SaveTelemetryConsentAsync([FromBody] TelemetryC
288295
}
289296

290297
/// <summary>
291-
/// Get current telemetry consent settings
298+
/// Get current telemetry consent settings.
292299
/// </summary>
300+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
293301
[HttpGet("telemetry-consent")]
294302
public async Task<ActionResult<TelemetryConsentDto>> GetTelemetryConsentAsync()
295303
{
@@ -310,4 +318,4 @@ public async Task<ActionResult<TelemetryConsentDto>> GetTelemetryConsentAsync()
310318
return StatusCode(500, new { message = "Internal server error" });
311319
}
312320
}
313-
}
321+
}

ThingConnect.Pulse.Server/Controllers/ConfigurationController.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ public ConfigurationController(IConfigurationService configurationService)
1818
}
1919

2020
/// <summary>
21-
/// Validate and apply YAML configuration
21+
/// Validate and apply YAML configuration.
2222
/// </summary>
23-
/// <param name="dryRun">If true, only validate and preview changes without applying</param>
24-
/// <returns>Apply result with counts of changes made or preview of changes</returns>
23+
/// <param name="dryRun">If true, only validate and preview changes without applying.</param>
24+
/// <returns>Apply result with counts of changes made or preview of changes.</returns>
2525
[HttpPost("apply")]
2626
[Authorize(Roles = UserRoles.Administrator)]
2727
public async Task<ActionResult<ApplyResultDto>> ApplyAsync([FromQuery] bool dryRun = false)
@@ -38,7 +38,7 @@ public async Task<ActionResult<ApplyResultDto>> ApplyAsync([FromQuery] bool dryR
3838
Message = "YAML content is required",
3939
Errors = new List<ValidationError>
4040
{
41-
new() { Path = "", Message = "YAML content cannot be empty", Value = null }
41+
new() { Path = string.Empty, Message = "YAML content cannot be empty", Value = null }
4242
}
4343
});
4444
}
@@ -69,7 +69,7 @@ public async Task<ActionResult<ApplyResultDto>> ApplyAsync([FromQuery] bool dryR
6969
Message = ex.Message,
7070
Errors = new List<ValidationError>
7171
{
72-
new() { Path = "", Message = ex.Message, Value = null }
72+
new() { Path = string.Empty, Message = ex.Message, Value = null }
7373
}
7474
});
7575
}
@@ -80,16 +80,16 @@ public async Task<ActionResult<ApplyResultDto>> ApplyAsync([FromQuery] bool dryR
8080
Message = "Internal server error while applying configuration",
8181
Errors = new List<ValidationError>
8282
{
83-
new() { Path = "", Message = ex.Message, Value = null }
83+
new() { Path = string.Empty, Message = ex.Message, Value = null }
8484
}
8585
});
8686
}
8787
}
8888

8989
/// <summary>
90-
/// List all configuration versions
90+
/// List all configuration versions.
9191
/// </summary>
92-
/// <returns>List of configuration versions ordered by applied timestamp descending</returns>
92+
/// <returns>List of configuration versions ordered by applied timestamp descending.</returns>
9393
[HttpGet("versions")]
9494
[Authorize]
9595
public async Task<ActionResult<List<ConfigurationVersionDto>>> GetVersionsAsync()
@@ -106,10 +106,10 @@ public async Task<ActionResult<List<ConfigurationVersionDto>>> GetVersionsAsync(
106106
}
107107

108108
/// <summary>
109-
/// Download a specific configuration version as YAML
109+
/// Download a specific configuration version as YAML.
110110
/// </summary>
111-
/// <param name="id">Configuration version ID</param>
112-
/// <returns>Plain YAML content</returns>
111+
/// <param name="id">Configuration version ID.</param>
112+
/// <returns>Plain YAML content.</returns>
113113
[HttpGet("versions/{id}")]
114114
[Authorize]
115115
public async Task<ActionResult> GetVersionAsync(string id)
@@ -131,9 +131,9 @@ public async Task<ActionResult> GetVersionAsync(string id)
131131
}
132132

133133
/// <summary>
134-
/// Get the current active configuration as YAML
134+
/// Get the current active configuration as YAML.
135135
/// </summary>
136-
/// <returns>Plain YAML content of the active configuration</returns>
136+
/// <returns>Plain YAML content of the active configuration.</returns>
137137
[HttpGet("current")]
138138
[Authorize]
139139
public async Task<ActionResult> GetCurrentAsync()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using ThingConnect.Pulse.Server.Services;
3+
4+
[ApiController]
5+
[Route("api/endpoints")]
6+
public sealed class EndpointsController : ControllerBase
7+
{
8+
private readonly IEndpointService _endpointService;
9+
10+
public EndpointsController(IEndpointService endpointService)
11+
{
12+
_endpointService = endpointService;
13+
}
14+
15+
[HttpGet("{id:guid}")]
16+
public async Task<ActionResult<EndpointDetailDto>> GetEndpointDetail(
17+
Guid id,
18+
[FromQuery] int windowMinutes = 60)
19+
{
20+
var detail = await _endpointService.GetEndpointDetailAsync(id, windowMinutes);
21+
return detail == null ? (ActionResult<EndpointDetailDto>)NotFound() : (ActionResult<EndpointDetailDto>)Ok(detail);
22+
}
23+
}

ThingConnect.Pulse.Server/Controllers/HistoryController.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public HistoryController(IHistoryService historyService, ILogger<HistoryControll
2020
}
2121

2222
/// <summary>
23-
/// Get historical data for a specific endpoint
23+
/// Get historical data for a specific endpoint.
2424
/// </summary>
25-
/// <param name="id">Endpoint ID</param>
26-
/// <param name="from">Start time (ISO 8601 format)</param>
27-
/// <param name="to">End time (ISO 8601 format)</param>
28-
/// <param name="bucket">Data bucket type: raw, 15m, or daily</param>
29-
/// <returns>Historical data for the endpoint</returns>
25+
/// <param name="id">Endpoint ID.</param>
26+
/// <param name="from">Start time (ISO 8601 format).</param>
27+
/// <param name="to">End time (ISO 8601 format).</param>
28+
/// <param name="bucket">Data bucket type: raw, 15m, or daily.</param>
29+
/// <returns>Historical data for the endpoint.</returns>
3030
[HttpGet("endpoint/{id}")]
3131
public async Task<ActionResult<HistoryResponseDto>> GetEndpointHistoryAsync(
3232
[FromRoute] Guid id,
@@ -88,4 +88,4 @@ public async Task<ActionResult<HistoryResponseDto>> GetEndpointHistoryAsync(
8888
return StatusCode(500, new { message = "Internal server error while retrieving endpoint history" });
8989
}
9090
}
91-
}
91+
}

ThingConnect.Pulse.Server/Controllers/StatusController.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public StatusController(IStatusService statusService, ILogger<StatusController>
2020
}
2121

2222
/// <summary>
23-
/// Get paged live status feed for all endpoints
23+
/// Get paged live status feed for all endpoints.
2424
/// </summary>
25-
/// <param name="group">Optional group ID filter</param>
26-
/// <param name="search">Optional search string (matches name or host)</param>
27-
/// <returns>Paged list of endpoint status with sparkline data</returns>
25+
/// <param name="group">Optional group ID filter.</param>
26+
/// <param name="search">Optional search string (matches name or host).</param>
27+
/// <returns>Paged list of endpoint status with sparkline data.</returns>
2828
[HttpGet("live")]
2929
public async Task<ActionResult<LiveStatusItemDto>> GetLiveStatusAsync(
3030
[FromQuery] string? group = null,
@@ -35,10 +35,10 @@ public async Task<ActionResult<LiveStatusItemDto>> GetLiveStatusAsync(
3535
_logger.LogInformation("Getting live status - group: {Group}, search: {Search}, page: {Page}, pageSize: {PageSize}",
3636
group, search);
3737

38-
var result = await _statusService.GetLiveStatusAsync(group, search);
39-
40-
return Ok(new { items = result });
41-
}
38+
List<LiveStatusItemDto> result = await _statusService.GetLiveStatusAsync(group, search);
39+
40+
return Ok(new { items = result });
41+
}
4242
catch (Exception ex)
4343
{
4444
_logger.LogError(ex, "Error getting live status");

ThingConnect.Pulse.Server/Controllers/UserManagementController.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ public UserManagementController(
2424
}
2525

2626
/// <summary>
27-
/// Get all users with pagination
27+
/// Get all users with pagination.
2828
/// </summary>
29+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
2930
[HttpGet]
3031
public async Task<ActionResult<PagedResult<UserInfoDto>>> GetUsersAsync(
3132
[FromQuery] int page = 1,
@@ -36,8 +37,15 @@ public async Task<ActionResult<PagedResult<UserInfoDto>>> GetUsersAsync(
3637
{
3738
try
3839
{
39-
if (page < 1) page = 1;
40-
if (pageSize < 1 || pageSize > 100) pageSize = 20;
40+
if (page < 1)
41+
{
42+
page = 1;
43+
}
44+
45+
if (pageSize < 1 || pageSize > 100)
46+
{
47+
pageSize = 20;
48+
}
4149

4250
IQueryable<ApplicationUser> query = _userManager.Users.AsQueryable();
4351

@@ -97,8 +105,9 @@ public async Task<ActionResult<PagedResult<UserInfoDto>>> GetUsersAsync(
97105
}
98106

99107
/// <summary>
100-
/// Get user by ID
108+
/// Get user by ID.
101109
/// </summary>
110+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
102111
[HttpGet("{id}")]
103112
public async Task<ActionResult<UserInfoDto>> GetUserByIdAsync(string id)
104113
{
@@ -129,8 +138,9 @@ public async Task<ActionResult<UserInfoDto>> GetUserByIdAsync(string id)
129138
}
130139

131140
/// <summary>
132-
/// Create a new user
141+
/// Create a new user.
133142
/// </summary>
143+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
134144
[HttpPost]
135145
public async Task<ActionResult<UserInfoDto>> CreateUserAsync([FromBody] CreateUserDto request)
136146
{
@@ -196,8 +206,9 @@ public async Task<ActionResult<UserInfoDto>> CreateUserAsync([FromBody] CreateUs
196206
}
197207

198208
/// <summary>
199-
/// Update user details
209+
/// Update user details.
200210
/// </summary>
211+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
201212
[HttpPut("{id}")]
202213
public async Task<ActionResult<UserInfoDto>> UpdateUserAsync(string id, [FromBody] UpdateUserDto request)
203214
{
@@ -285,8 +296,9 @@ public async Task<ActionResult<UserInfoDto>> UpdateUserAsync(string id, [FromBod
285296
}
286297

287298
/// <summary>
288-
/// Change user role
299+
/// Change user role.
289300
/// </summary>
301+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
290302
[HttpPut("{id}/role")]
291303
public async Task<ActionResult<UserInfoDto>> ChangeUserRoleAsync(string id, [FromBody] ChangeRoleDto request)
292304
{
@@ -348,8 +360,9 @@ public async Task<ActionResult<UserInfoDto>> ChangeUserRoleAsync(string id, [Fro
348360
}
349361

350362
/// <summary>
351-
/// Reset user password (admin only)
363+
/// Reset user password (admin only).
352364
/// </summary>
365+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
353366
[HttpPost("{id}/reset-password")]
354367
public async Task<IActionResult> ResetPasswordAsync(string id, [FromBody] ResetPasswordDto request)
355368
{
@@ -389,8 +402,9 @@ public async Task<IActionResult> ResetPasswordAsync(string id, [FromBody] ResetP
389402
}
390403

391404
/// <summary>
392-
/// Delete user (soft delete - deactivate)
405+
/// Delete user (soft delete - deactivate).
393406
/// </summary>
407+
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
394408
[HttpDelete("{id}")]
395409
public async Task<IActionResult> DeleteUserAsync(string id)
396410
{
@@ -451,4 +465,4 @@ public sealed class PagedResult<T>
451465
public int PageSize { get; set; }
452466
public int TotalCount { get; set; }
453467
public int TotalPages { get; set; }
454-
}
468+
}

ThingConnect.Pulse.Server/Data/ApplicationUser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ public static class UserRoles
1717
public const string User = "User";
1818

1919
public static readonly string[] AllRoles = { Administrator, User };
20-
}
20+
}

0 commit comments

Comments
 (0)