Skip to content

Commit 0593df4

Browse files
committed
Merge branch 'master' of https://github.com/MachDatum/ThingConnect.Pulse into history
2 parents 1d71484 + d8aca75 commit 0593df4

37 files changed

+1107
-314
lines changed

.claude/settings.local.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
"Bash(curl:*)",
88
"Bash(taskkill:*)",
99
"mcp__chakra-ui",
10-
"mcp__serena"
10+
"mcp__serena",
11+
"Bash(sqlite3:*)",
12+
"Bash(dir:*)",
13+
"Bash(timeout:*)"
1114
],
1215
"deny": [],
1316
"ask": []

ThingConnect.Pulse.Server/Controllers/AuthController.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public async Task<ActionResult<UserInfoDto>> LoginAsync([FromBody] LoginRequestD
3232
{
3333
try
3434
{
35-
var user = await _userManager.FindByNameAsync(request.Username);
35+
ApplicationUser? user = await _userManager.FindByNameAsync(request.Username);
3636
if (user == null)
3737
{
3838
_logger.LogWarning("Login attempt for non-existent user: {Username}", request.Username);
@@ -45,16 +45,16 @@ public async Task<ActionResult<UserInfoDto>> LoginAsync([FromBody] LoginRequestD
4545
return Unauthorized(new { message = "Account is inactive" });
4646
}
4747

48-
var result = await _signInManager.PasswordSignInAsync(user, request.Password, isPersistent: true, lockoutOnFailure: true);
49-
48+
Microsoft.AspNetCore.Identity.SignInResult result = await _signInManager.PasswordSignInAsync(user, request.Password, isPersistent: true, lockoutOnFailure: true);
49+
5050
if (!result.Succeeded)
5151
{
5252
if (result.IsLockedOut)
5353
{
5454
_logger.LogWarning("Account locked out for user: {Username}", request.Username);
5555
return Unauthorized(new { message = "Account is locked due to multiple failed login attempts" });
5656
}
57-
57+
5858
_logger.LogWarning("Invalid login attempt for user: {Username}", request.Username);
5959
return Unauthorized(new { message = "Invalid username or password" });
6060
}
@@ -92,7 +92,7 @@ public async Task<ActionResult<UserInfoDto>> RegisterAsync([FromBody] RegisterRe
9292
try
9393
{
9494
// Only allow registration if no users exist (initial admin setup)
95-
var userCount = _userManager.Users.Count();
95+
int userCount = _userManager.Users.Count();
9696
if (userCount > 0)
9797
{
9898
_logger.LogWarning("Registration attempt when users already exist");
@@ -109,7 +109,7 @@ public async Task<ActionResult<UserInfoDto>> RegisterAsync([FromBody] RegisterRe
109109
IsActive = true
110110
};
111111

112-
var result = await _userManager.CreateAsync(user, request.Password);
112+
IdentityResult result = await _userManager.CreateAsync(user, request.Password);
113113

114114
if (!result.Succeeded)
115115
{
@@ -146,7 +146,7 @@ public ActionResult<bool> IsSetupRequired()
146146
{
147147
try
148148
{
149-
var userCount = _userManager.Users.Count();
149+
int userCount = _userManager.Users.Count();
150150
return Ok(userCount == 0);
151151
}
152152
catch (Exception ex)
@@ -165,7 +165,7 @@ public async Task<ActionResult<UserInfoDto>> GetSessionAsync()
165165
{
166166
try
167167
{
168-
var user = await _userManager.GetUserAsync(User);
168+
ApplicationUser? user = await _userManager.GetUserAsync(User);
169169
if (user == null || !user.IsActive)
170170
{
171171
return Unauthorized(new { message = "Session invalid or user inactive" });
@@ -198,13 +198,13 @@ public async Task<IActionResult> ChangePasswordAsync([FromBody] ChangePasswordDt
198198
{
199199
try
200200
{
201-
var user = await _userManager.GetUserAsync(User);
201+
ApplicationUser? user = await _userManager.GetUserAsync(User);
202202
if (user == null || !user.IsActive)
203203
{
204204
return Unauthorized(new { message = "Session invalid or user inactive" });
205205
}
206206

207-
var result = await _userManager.ChangePasswordAsync(user, request.CurrentPassword, request.NewPassword);
207+
IdentityResult result = await _userManager.ChangePasswordAsync(user, request.CurrentPassword, request.NewPassword);
208208
if (!result.Succeeded)
209209
{
210210
var errors = result.Errors.Select(e => e.Description).ToList();
@@ -234,14 +234,14 @@ public async Task<IActionResult> LogoutAsync()
234234
{
235235
try
236236
{
237-
var user = await _userManager.GetUserAsync(User);
237+
ApplicationUser? user = await _userManager.GetUserAsync(User);
238238
await _signInManager.SignOutAsync();
239-
239+
240240
if (user != null)
241241
{
242242
_logger.LogInformation("User {Username} (ID: {UserId}) logged out", user.UserName, user.Id);
243243
}
244-
244+
245245
return Ok(new { message = "Logged out successfully" });
246246
}
247247
catch (Exception ex)

ThingConnect.Pulse.Server/Controllers/ConfigurationController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public async Task<ActionResult<ApplyResultDto>> ApplyAsync([FromQuery] bool dryR
2828
{
2929
try
3030
{
31-
using StreamReader reader = new StreamReader(Request.Body);
31+
using var reader = new StreamReader(Request.Body);
3232
string yamlContent = await reader.ReadToEndAsync();
3333

3434
if (string.IsNullOrWhiteSpace(yamlContent))

ThingConnect.Pulse.Server/Controllers/UserManagementController.cs

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ public async Task<ActionResult<PagedResult<UserInfoDto>>> GetUsersAsync(
3939
if (page < 1) page = 1;
4040
if (pageSize < 1 || pageSize > 100) pageSize = 20;
4141

42-
var query = _userManager.Users.AsQueryable();
42+
IQueryable<ApplicationUser> query = _userManager.Users.AsQueryable();
4343

4444
// Apply filters
4545
if (!string.IsNullOrWhiteSpace(search))
4646
{
47-
var searchLower = search.ToLower();
48-
query = query.Where(u =>
49-
u.UserName!.ToLower().Contains(searchLower) ||
47+
string searchLower = search.ToLower();
48+
query = query.Where(u =>
49+
u.UserName!.ToLower().Contains(searchLower) ||
5050
u.Email!.ToLower().Contains(searchLower));
5151
}
5252

@@ -60,9 +60,9 @@ public async Task<ActionResult<PagedResult<UserInfoDto>>> GetUsersAsync(
6060
query = query.Where(u => u.IsActive == isActive.Value);
6161
}
6262

63-
var totalCount = await query.CountAsync();
64-
65-
var users = await query
63+
int totalCount = await query.CountAsync();
64+
65+
List<UserInfoDto> users = await query
6666
.OrderBy(u => u.UserName)
6767
.Skip((page - 1) * pageSize)
6868
.Take(pageSize)
@@ -104,7 +104,7 @@ public async Task<ActionResult<UserInfoDto>> GetUserByIdAsync(string id)
104104
{
105105
try
106106
{
107-
var user = await _userManager.FindByIdAsync(id);
107+
ApplicationUser? user = await _userManager.FindByIdAsync(id);
108108
if (user == null)
109109
{
110110
return NotFound(new { message = "User not found" });
@@ -143,7 +143,7 @@ public async Task<ActionResult<UserInfoDto>> CreateUserAsync([FromBody] CreateUs
143143
}
144144

145145
// Check if username or email already exists
146-
var existingUser = await _userManager.FindByNameAsync(request.Username);
146+
ApplicationUser? existingUser = await _userManager.FindByNameAsync(request.Username);
147147
if (existingUser != null)
148148
{
149149
return BadRequest(new { message = "Username already exists" });
@@ -165,16 +165,16 @@ public async Task<ActionResult<UserInfoDto>> CreateUserAsync([FromBody] CreateUs
165165
IsActive = true
166166
};
167167

168-
var result = await _userManager.CreateAsync(user, request.Password);
168+
IdentityResult result = await _userManager.CreateAsync(user, request.Password);
169169
if (!result.Succeeded)
170170
{
171171
var errors = result.Errors.Select(e => e.Description).ToList();
172172
_logger.LogWarning("User creation failed for {Username}: {Errors}", request.Username, string.Join(", ", errors));
173173
return BadRequest(new { message = "User creation failed", errors });
174174
}
175175

176-
var currentUser = await _userManager.GetUserAsync(User);
177-
_logger.LogInformation("User created: {Username} (ID: {UserId}) by admin {AdminId}",
176+
ApplicationUser? currentUser = await _userManager.GetUserAsync(User);
177+
_logger.LogInformation("User created: {Username} (ID: {UserId}) by admin {AdminId}",
178178
user.UserName, user.Id, currentUser?.Id);
179179

180180
return CreatedAtAction(nameof(GetUserByIdAsync), new { id = user.Id }, new UserInfoDto
@@ -203,14 +203,14 @@ public async Task<ActionResult<UserInfoDto>> UpdateUserAsync(string id, [FromBod
203203
{
204204
try
205205
{
206-
var user = await _userManager.FindByIdAsync(id);
206+
ApplicationUser? user = await _userManager.FindByIdAsync(id);
207207
if (user == null)
208208
{
209209
return NotFound(new { message = "User not found" });
210210
}
211211

212212
// Prevent admin from deactivating themselves
213-
var currentUser = await _userManager.GetUserAsync(User);
213+
ApplicationUser? currentUser = await _userManager.GetUserAsync(User);
214214
if (currentUser != null && id == currentUser.Id && request.IsActive == false)
215215
{
216216
return BadRequest(new { message = "Cannot deactivate your own account" });
@@ -221,25 +221,25 @@ public async Task<ActionResult<UserInfoDto>> UpdateUserAsync(string id, [FromBod
221221
if (!string.IsNullOrWhiteSpace(request.Username) && request.Username != user.UserName)
222222
{
223223
// Check if new username already exists
224-
var existingUser = await _userManager.FindByNameAsync(request.Username);
224+
ApplicationUser? existingUser = await _userManager.FindByNameAsync(request.Username);
225225
if (existingUser != null && existingUser.Id != id)
226226
{
227227
return BadRequest(new { message = "Username already exists" });
228228
}
229-
229+
230230
changes.Add($"Username: {user.UserName} -> {request.Username}");
231231
user.UserName = request.Username;
232232
}
233233

234234
if (!string.IsNullOrWhiteSpace(request.Email) && request.Email != user.Email)
235235
{
236236
// Check if new email already exists
237-
var existingUser = await _userManager.FindByEmailAsync(request.Email);
237+
ApplicationUser? existingUser = await _userManager.FindByEmailAsync(request.Email);
238238
if (existingUser != null && existingUser.Id != id)
239239
{
240240
return BadRequest(new { message = "Email already exists" });
241241
}
242-
242+
243243
changes.Add($"Email: {user.Email} -> {request.Email}");
244244
user.Email = request.Email;
245245
}
@@ -253,16 +253,16 @@ public async Task<ActionResult<UserInfoDto>> UpdateUserAsync(string id, [FromBod
253253
if (changes.Any())
254254
{
255255
user.UpdatedAt = DateTimeOffset.UtcNow;
256-
var result = await _userManager.UpdateAsync(user);
257-
256+
IdentityResult result = await _userManager.UpdateAsync(user);
257+
258258
if (!result.Succeeded)
259259
{
260260
var errors = result.Errors.Select(e => e.Description).ToList();
261261
_logger.LogWarning("User update failed for {UserId}: {Errors}", id, string.Join(", ", errors));
262262
return BadRequest(new { message = "User update failed", errors });
263263
}
264264

265-
_logger.LogInformation("User {UserId} updated by admin {AdminId}. Changes: {Changes}",
265+
_logger.LogInformation("User {UserId} updated by admin {AdminId}. Changes: {Changes}",
266266
id, currentUser?.Id, string.Join("; ", changes));
267267
}
268268

@@ -297,36 +297,36 @@ public async Task<ActionResult<UserInfoDto>> ChangeUserRoleAsync(string id, [Fro
297297
return BadRequest(new { message = $"Invalid role. Allowed roles: {string.Join(", ", UserRoles.AllRoles)}" });
298298
}
299299

300-
var user = await _userManager.FindByIdAsync(id);
300+
ApplicationUser? user = await _userManager.FindByIdAsync(id);
301301
if (user == null)
302302
{
303303
return NotFound(new { message = "User not found" });
304304
}
305305

306306
// Prevent admin from changing their own role if they're the only admin
307-
var currentUser = await _userManager.GetUserAsync(User);
307+
ApplicationUser? currentUser = await _userManager.GetUserAsync(User);
308308
if (currentUser != null && id == currentUser.Id && user.Role == UserRoles.Administrator && request.Role != UserRoles.Administrator)
309309
{
310-
var adminCount = await _userManager.Users.CountAsync(u => u.Role == UserRoles.Administrator && u.IsActive);
310+
int adminCount = await _userManager.Users.CountAsync(u => u.Role == UserRoles.Administrator && u.IsActive);
311311
if (adminCount <= 1)
312312
{
313313
return BadRequest(new { message = "Cannot change role - you are the only active administrator" });
314314
}
315315
}
316316

317-
var oldRole = user.Role;
317+
string oldRole = user.Role;
318318
user.Role = request.Role;
319319
user.UpdatedAt = DateTimeOffset.UtcNow;
320320

321-
var result = await _userManager.UpdateAsync(user);
321+
IdentityResult result = await _userManager.UpdateAsync(user);
322322
if (!result.Succeeded)
323323
{
324324
var errors = result.Errors.Select(e => e.Description).ToList();
325325
_logger.LogWarning("Role change failed for user {UserId}: {Errors}", id, string.Join(", ", errors));
326326
return BadRequest(new { message = "Role change failed", errors });
327327
}
328328

329-
_logger.LogInformation("User {UserId} role changed from {OldRole} to {NewRole} by admin {AdminId}",
329+
_logger.LogInformation("User {UserId} role changed from {OldRole} to {NewRole} by admin {AdminId}",
330330
id, oldRole, request.Role, currentUser?.Id);
331331

332332
return Ok(new UserInfoDto
@@ -355,16 +355,16 @@ public async Task<IActionResult> ResetPasswordAsync(string id, [FromBody] ResetP
355355
{
356356
try
357357
{
358-
var user = await _userManager.FindByIdAsync(id);
358+
ApplicationUser? user = await _userManager.FindByIdAsync(id);
359359
if (user == null)
360360
{
361361
return NotFound(new { message = "User not found" });
362362
}
363363

364364
// Remove current password and set new one
365-
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
366-
var result = await _userManager.ResetPasswordAsync(user, token, request.NewPassword);
367-
365+
string token = await _userManager.GeneratePasswordResetTokenAsync(user);
366+
IdentityResult result = await _userManager.ResetPasswordAsync(user, token, request.NewPassword);
367+
368368
if (!result.Succeeded)
369369
{
370370
var errors = result.Errors.Select(e => e.Description).ToList();
@@ -375,8 +375,8 @@ public async Task<IActionResult> ResetPasswordAsync(string id, [FromBody] ResetP
375375
user.UpdatedAt = DateTimeOffset.UtcNow;
376376
await _userManager.UpdateAsync(user);
377377

378-
var currentUser = await _userManager.GetUserAsync(User);
379-
_logger.LogInformation("Password reset for user {UserId} by admin {AdminId}",
378+
ApplicationUser? currentUser = await _userManager.GetUserAsync(User);
379+
_logger.LogInformation("Password reset for user {UserId} by admin {AdminId}",
380380
id, currentUser?.Id);
381381

382382
return Ok(new { message = "Password reset successfully" });
@@ -396,14 +396,14 @@ public async Task<IActionResult> DeleteUserAsync(string id)
396396
{
397397
try
398398
{
399-
var user = await _userManager.FindByIdAsync(id);
399+
ApplicationUser? user = await _userManager.FindByIdAsync(id);
400400
if (user == null)
401401
{
402402
return NotFound(new { message = "User not found" });
403403
}
404404

405405
// Prevent admin from deleting themselves
406-
var currentUser = await _userManager.GetUserAsync(User);
406+
ApplicationUser? currentUser = await _userManager.GetUserAsync(User);
407407
if (currentUser != null && id == currentUser.Id)
408408
{
409409
return BadRequest(new { message = "Cannot delete your own account" });
@@ -412,7 +412,7 @@ public async Task<IActionResult> DeleteUserAsync(string id)
412412
// Check if this is the only active admin
413413
if (user.Role == UserRoles.Administrator && user.IsActive)
414414
{
415-
var adminCount = await _userManager.Users.CountAsync(u => u.Role == UserRoles.Administrator && u.IsActive);
415+
int adminCount = await _userManager.Users.CountAsync(u => u.Role == UserRoles.Administrator && u.IsActive);
416416
if (adminCount <= 1)
417417
{
418418
return BadRequest(new { message = "Cannot delete the only active administrator" });
@@ -423,15 +423,15 @@ public async Task<IActionResult> DeleteUserAsync(string id)
423423
user.IsActive = false;
424424
user.UpdatedAt = DateTimeOffset.UtcNow;
425425

426-
var result = await _userManager.UpdateAsync(user);
426+
IdentityResult result = await _userManager.UpdateAsync(user);
427427
if (!result.Succeeded)
428428
{
429429
var errors = result.Errors.Select(e => e.Description).ToList();
430430
_logger.LogWarning("User deletion failed for {UserId}: {Errors}", id, string.Join(", ", errors));
431431
return BadRequest(new { message = "User deletion failed", errors });
432432
}
433433

434-
_logger.LogInformation("User {UserId} ({Username}) deactivated by admin {AdminId}",
434+
_logger.LogInformation("User {UserId} ({Username}) deactivated by admin {AdminId}",
435435
id, user.UserName, currentUser?.Id);
436436

437437
return Ok(new { message = "User deactivated successfully" });

ThingConnect.Pulse.Server/Data/ApplicationUser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ public static class UserRoles
1515
{
1616
public const string Administrator = "Administrator";
1717
public const string User = "User";
18-
18+
1919
public static readonly string[] AllRoles = { Administrator, User };
2020
}

0 commit comments

Comments
 (0)