From 8249b3fe3e9e65840827f77d90efec060652afb7 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Wed, 27 May 2026 14:10:46 -0700 Subject: [PATCH] feat: add UsersController and register all services Adds UsersController with CRUD endpoints (list, get, create, update, deactivate, reset-password) restricted to super_admin role. Registers IUserManagementService in Program.cs alongside existing services. Co-Authored-By: Claude Sonnet 4.6 --- API/ROLAC.API/Controllers/UsersController.cs | 78 ++++++++++++++++++++ API/ROLAC.API/Program.cs | 7 +- 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 API/ROLAC.API/Controllers/UsersController.cs diff --git a/API/ROLAC.API/Controllers/UsersController.cs b/API/ROLAC.API/Controllers/UsersController.cs new file mode 100644 index 0000000..9bf0e65 --- /dev/null +++ b/API/ROLAC.API/Controllers/UsersController.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using ROLAC.API.DTOs.Users; +using ROLAC.API.Services; + +namespace ROLAC.API.Controllers; + +[ApiController] +[Route("api/users")] +[Authorize(Roles = "super_admin")] +public class UsersController : ControllerBase +{ + private readonly IUserManagementService _users; + public UsersController(IUserManagementService users) => _users = users; + + /// GET /api/users?page=1&pageSize=20&search=Chris + [HttpGet] + public async Task GetPaged( + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20, + [FromQuery] string? search = null) + => Ok(await _users.GetPagedAsync(page, pageSize, search)); + + /// GET /api/users/{id} + [HttpGet("{id}")] + public async Task GetById(string id) + { + var dto = await _users.GetByIdAsync(id); + return dto is null ? NotFound() : Ok(dto); + } + + /// + /// POST /api/users — creates account for a Member, returns { userId, tempPassword }. + /// TempPassword is returned ONCE — show it to the admin and never log it. + /// + [HttpPost] + public async Task Create([FromBody] CreateUserRequest request) + { + try + { + var result = await _users.CreateAsync(request); + return Ok(result); + } + catch (InvalidOperationException ex) + { + return BadRequest(new { message = ex.Message }); + } + } + + /// PUT /api/users/{id} — update email, roles, IsActive + [HttpPut("{id}")] + public async Task Update(string id, [FromBody] UpdateUserRequest request) + { + try { await _users.UpdateAsync(id, request); return NoContent(); } + catch (KeyNotFoundException) { return NotFound(); } + catch (InvalidOperationException ex) { return BadRequest(new { message = ex.Message }); } + } + + /// DELETE /api/users/{id} — deactivates account (IsActive=false), does not delete + [HttpDelete("{id}")] + public async Task Deactivate(string id) + { + try { await _users.DeactivateAsync(id); return NoContent(); } + catch (KeyNotFoundException) { return NotFound(); } + } + + /// POST /api/users/{id}/reset-password — returns new temp password + [HttpPost("{id}/reset-password")] + public async Task ResetPassword(string id) + { + try + { + var pwd = await _users.ResetPasswordAsync(id); + return Ok(new { tempPassword = pwd }); + } + catch (KeyNotFoundException) { return NotFound(); } + } +} diff --git a/API/ROLAC.API/Program.cs b/API/ROLAC.API/Program.cs index 3272ca5..ba966ba 100644 --- a/API/ROLAC.API/Program.cs +++ b/API/ROLAC.API/Program.cs @@ -82,9 +82,10 @@ builder.Services.AddCors(opt => // --------------------------------------------------------------------------- // Application services // --------------------------------------------------------------------------- -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); // --------------------------------------------------------------------------- // Swagger / MVC