This commit is contained in:
Dhananjaya99
2026-07-18 23:42:58 +05:30
parent 80b130dffb
commit 92c4b14a6c
55 changed files with 8815 additions and 43 deletions
+17 -1
View File
@@ -1,4 +1,5 @@
using ERPCore.Dtos.Auth;
using ERPCore.Dtos.Rbac;
using ERPCore.Infra.Auth;
using ERPCore.Services.Interfaces;
using ERPCore.System.Errors;
@@ -25,12 +26,27 @@ public sealed class AuthController : ControllerBase
private readonly IAuthUserService _users;
private readonly IAuthRecoveryService _recovery;
private readonly IAuthAltService _alt;
private readonly IRoleService _roles;
public AuthController(IAuthUserService users, IAuthRecoveryService recovery, IAuthAltService alt)
public AuthController(IAuthUserService users, IAuthRecoveryService recovery, IAuthAltService alt, IRoleService roles)
{
_users = users;
_recovery = recovery;
_alt = alt;
_roles = roles;
}
/// <summary>
/// Authoritative current-session info for the frontend: role + the sidebar nav
/// codes it may see (docs/10 C.9 shadow-Role sync). Replaces the frontend's
/// previous reliance on a stale, untrusted `roleId` cached in localStorage.
/// </summary>
[HttpGet("me")]
[ProducesResponseType(typeof(MeResponseDto), StatusCodes.Status200OK)]
public async Task<ActionResult<MeResponseDto>> Me(CancellationToken ct)
{
var roleCode = User.FindFirst(AuthHexClaims.RoleCode)?.Value;
return Ok(await _roles.GetMeAsync(roleCode, ct));
}
// ---- Session-issuing (UserManager) ------------------------------------
@@ -0,0 +1,39 @@
using ERPCore.Domain.Entities;
using ERPCore.Dtos.Rbac;
using ERPCore.Repositories.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace ERPCore.Controllers;
/// <summary>
/// Read-only sidebar nav tree, used by the Role permission-assignment checkbox
/// UI and by `GET /auth/me` (see AuthController) to resolve a role's visible codes.
/// NavItem/SubNavItem rows are seeded (NavItemConfiguration/SubNavItemConfiguration)
/// to match the frontend's hardcoded sidebar — not admin-editable in this phase.
/// </summary>
[Route("api/v1/nav")]
public sealed class NavController : ApiControllerBase
{
private readonly IRepository<NavItem> _navItems;
public NavController(IRepository<NavItem> navItems) => _navItems = navItems;
[HttpGet]
[ProducesResponseType(typeof(List<NavItemDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<List<NavItemDto>>> GetTree(CancellationToken ct)
{
var items = await _navItems.Query().AsNoTracking()
.Include(n => n.Children)
.OrderBy(n => n.SortOrder)
.ToListAsync(ct);
var dto = items.Select(n => new NavItemDto(
n.NavItemId, n.Code, n.Label, n.Icon, n.Href, n.SortOrder,
n.Children.OrderBy(c => c.SortOrder)
.Select(c => new SubNavItemDto(c.SubNavItemId, c.Code, c.Label, c.Icon, c.Href, c.SortOrder))
.ToList())).ToList();
return Ok(dto);
}
}
@@ -0,0 +1,88 @@
using ERPCore.Domain.Enums;
using ERPCore.Dtos.Common;
using ERPCore.Dtos.Rbac;
using ERPCore.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace ERPCore.Controllers;
/// <summary>Role CRUD + permission assignment (mirrors AuthHex's Role; see docs/10 C.9).</summary>
[Route("api/v1/roles")]
public sealed class RolesController : ApiControllerBase
{
private readonly IRoleService _roles;
public RolesController(IRoleService roles) => _roles = roles;
[HttpGet]
[ProducesResponseType(typeof(PagedResponse<RoleDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<PagedResponse<RoleDto>>> List(
[FromQuery] PageQuery query, [FromQuery] EntityStatus? status, CancellationToken ct)
=> Ok(await _roles.ListAsync(query, status, ct));
[HttpGet("{roleId:int}")]
[ProducesResponseType(typeof(RoleDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<RoleDto>> GetById(int roleId, CancellationToken ct)
{
var result = await _roles.GetAsync(roleId, ct);
if (result is null) return NotFound();
SetETag(result.RowVersion);
return Ok(result.Value);
}
[HttpPost]
[ProducesResponseType(typeof(RoleDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<RoleDto>> Create([FromBody] CreateRoleRequest request, CancellationToken ct)
{
var result = await _roles.CreateAsync(request, ct);
SetETag(result.RowVersion);
return Created($"/api/v1/roles/{result.Value.RoleId}", result.Value);
}
[HttpPut("{roleId:int}")]
[ProducesResponseType(typeof(RoleDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status412PreconditionFailed)]
public async Task<ActionResult<RoleDto>> Update(int roleId, [FromBody] UpdateRoleRequest request, CancellationToken ct)
{
var expected = RequireIfMatch();
var result = await _roles.UpdateAsync(roleId, request, expected, ct);
SetETag(result.RowVersion);
return Ok(result.Value);
}
[HttpPatch("{roleId:int}/status")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> SetStatus(int roleId, [FromBody] UpdateRoleStatusRequest request, CancellationToken ct)
{
await _roles.SetStatusAsync(roleId, request.Status, ct);
return NoContent();
}
[HttpDelete("{roleId:int}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<IActionResult> Delete(int roleId, CancellationToken ct)
{
await _roles.DeleteAsync(roleId, ct);
return NoContent();
}
[HttpGet("{roleId:int}/permissions")]
[ProducesResponseType(typeof(RolePermissionsDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<RolePermissionsDto>> GetPermissions(int roleId, CancellationToken ct)
=> Ok(await _roles.GetPermissionsAsync(roleId, ct));
[HttpPut("{roleId:int}/permissions")]
[ProducesResponseType(typeof(RolePermissionsDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<RolePermissionsDto>> AssignPermissions(
int roleId, [FromBody] AssignRolePermissionsRequest request, CancellationToken ct)
=> Ok(await _roles.AssignPermissionsAsync(roleId, request, ct));
}
@@ -0,0 +1,53 @@
using ERPCore.Dtos.Common;
using ERPCore.Dtos.Users;
using ERPCore.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace ERPCore.Controllers;
/// <summary>
/// User management: local shadow `User` list/detail + role assignment, and
/// account creation orchestrated against AuthHex (see <see cref="IUserManagementService.CreateAsync"/>).
/// </summary>
[Route("api/v1/users")]
public sealed class UsersController : ApiControllerBase
{
private readonly IUserManagementService _users;
public UsersController(IUserManagementService users) => _users = users;
[HttpGet]
[ProducesResponseType(typeof(PagedResponse<ManagedUserDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<PagedResponse<ManagedUserDto>>> List([FromQuery] PageQuery query, CancellationToken ct)
=> Ok(await _users.ListAsync(query, ct));
/// <summary>AuthHex UserType options for the create-user form's select.</summary>
[HttpGet("user-types")]
[ProducesResponseType(typeof(List<UserTypeOptionDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<List<UserTypeOptionDto>>> ListUserTypes(CancellationToken ct)
=> Ok(await _users.ListUserTypesAsync(ct));
[HttpGet("{userId:int}")]
[ProducesResponseType(typeof(ManagedUserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ManagedUserDto>> GetById(int userId, CancellationToken ct)
{
var result = await _users.GetAsync(userId, ct);
return result is null ? NotFound() : Ok(result);
}
[HttpPost]
[ProducesResponseType(typeof(ManagedUserDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<ActionResult<ManagedUserDto>> Create([FromBody] CreateUserRequest request, CancellationToken ct)
{
var result = await _users.CreateAsync(request, ct);
return Created($"/api/v1/users/{result.UserId}", result);
}
[HttpPut("{userId:int}/role")]
[ProducesResponseType(typeof(ManagedUserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ManagedUserDto>> UpdateRole(int userId, [FromBody] UpdateUserRoleRequest request, CancellationToken ct)
=> Ok(await _users.UpdateRoleAsync(userId, request, ct));
}