193 lines
6.1 KiB
C#
193 lines
6.1 KiB
C#
using System.Text.Json;
|
|
using line_gestao_api.Data;
|
|
using line_gestao_api.Dtos;
|
|
using line_gestao_api.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Globalization;
|
|
|
|
namespace line_gestao_api.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/historico")]
|
|
[Authorize(Roles = "admin")]
|
|
public class HistoricoController : ControllerBase
|
|
{
|
|
private readonly AppDbContext _db;
|
|
|
|
public HistoricoController(AppDbContext db)
|
|
{
|
|
_db = db;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<PagedResult<AuditLogDto>>> GetAll(
|
|
[FromQuery] string? pageName,
|
|
[FromQuery] string? action,
|
|
[FromQuery] string? entity,
|
|
[FromQuery] Guid? userId,
|
|
[FromQuery] string? search,
|
|
[FromQuery] DateTime? dateFrom,
|
|
[FromQuery] DateTime? dateTo,
|
|
[FromQuery] int page = 1,
|
|
[FromQuery] int pageSize = 20)
|
|
{
|
|
page = page < 1 ? 1 : page;
|
|
pageSize = pageSize < 1 ? 20 : pageSize;
|
|
|
|
var q = _db.AuditLogs
|
|
.AsNoTracking()
|
|
.Where(x =>
|
|
!EF.Functions.ILike(x.RequestPath ?? "", "%import-excel%") ||
|
|
x.Page == AuditLogBuilder.SpreadsheetImportPageName);
|
|
|
|
if (!string.IsNullOrWhiteSpace(pageName))
|
|
{
|
|
var p = pageName.Trim();
|
|
q = q.Where(x => EF.Functions.ILike(x.Page, $"%{p}%"));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(action))
|
|
{
|
|
var a = action.Trim().ToUpperInvariant();
|
|
q = q.Where(x => x.Action == a);
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(entity))
|
|
{
|
|
var e = entity.Trim();
|
|
q = q.Where(x => EF.Functions.ILike(x.EntityName, $"%{e}%"));
|
|
}
|
|
|
|
if (userId.HasValue)
|
|
{
|
|
q = q.Where(x => x.UserId == userId.Value);
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
var s = search.Trim();
|
|
var hasGuidSearch = Guid.TryParse(s, out var searchGuid);
|
|
var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc);
|
|
var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue;
|
|
q = q.Where(x =>
|
|
EF.Functions.ILike(x.UserName ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.UserEmail ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.Action ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.EntityName ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.EntityLabel ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.EntityId ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.Page ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.RequestPath ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.RequestMethod ?? "", $"%{s}%") ||
|
|
EF.Functions.ILike(x.IpAddress ?? "", $"%{s}%") ||
|
|
// ChangesJson is stored as jsonb; applying ILIKE directly causes PostgreSQL 42883.
|
|
(hasGuidSearch && (x.Id == searchGuid || x.UserId == searchGuid)) ||
|
|
(hasDateSearch &&
|
|
x.OccurredAtUtc >= searchDateStartUtc &&
|
|
x.OccurredAtUtc < searchDateEndUtc));
|
|
}
|
|
|
|
if (dateFrom.HasValue)
|
|
{
|
|
var fromUtc = ToUtc(dateFrom.Value);
|
|
q = q.Where(x => x.OccurredAtUtc >= fromUtc);
|
|
}
|
|
|
|
if (dateTo.HasValue)
|
|
{
|
|
var toUtc = ToUtc(dateTo.Value);
|
|
if (dateTo.Value.TimeOfDay == TimeSpan.Zero)
|
|
{
|
|
toUtc = toUtc.Date.AddDays(1).AddTicks(-1);
|
|
}
|
|
|
|
q = q.Where(x => x.OccurredAtUtc <= toUtc);
|
|
}
|
|
|
|
var total = await q.CountAsync();
|
|
|
|
var items = await q
|
|
.OrderByDescending(x => x.OccurredAtUtc)
|
|
.ThenByDescending(x => x.Id)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.ToListAsync();
|
|
|
|
return Ok(new PagedResult<AuditLogDto>
|
|
{
|
|
Page = page,
|
|
PageSize = pageSize,
|
|
Total = total,
|
|
Items = items.Select(ToDto).ToList()
|
|
});
|
|
}
|
|
|
|
private static AuditLogDto ToDto(Models.AuditLog log)
|
|
{
|
|
return new AuditLogDto
|
|
{
|
|
Id = log.Id,
|
|
OccurredAtUtc = log.OccurredAtUtc,
|
|
Action = log.Action,
|
|
Page = log.Page,
|
|
EntityName = log.EntityName,
|
|
EntityId = log.EntityId,
|
|
EntityLabel = log.EntityLabel,
|
|
UserId = log.UserId,
|
|
UserName = log.UserName,
|
|
UserEmail = log.UserEmail,
|
|
RequestPath = log.RequestPath,
|
|
RequestMethod = log.RequestMethod,
|
|
IpAddress = log.IpAddress,
|
|
Changes = ParseChanges(log.ChangesJson)
|
|
};
|
|
}
|
|
|
|
private static List<AuditFieldChangeDto> ParseChanges(string? json)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json))
|
|
{
|
|
return new List<AuditFieldChangeDto>();
|
|
}
|
|
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<List<AuditFieldChangeDto>>(json) ?? new List<AuditFieldChangeDto>();
|
|
}
|
|
catch
|
|
{
|
|
return new List<AuditFieldChangeDto>();
|
|
}
|
|
}
|
|
|
|
private static DateTime ToUtc(DateTime value)
|
|
{
|
|
if (value.Kind == DateTimeKind.Utc)
|
|
return value;
|
|
if (value.Kind == DateTimeKind.Local)
|
|
return value.ToUniversalTime();
|
|
|
|
return DateTime.SpecifyKind(value, DateTimeKind.Utc);
|
|
}
|
|
|
|
private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart)
|
|
{
|
|
utcStart = default;
|
|
if (string.IsNullOrWhiteSpace(value)) return false;
|
|
|
|
var s = value.Trim();
|
|
DateTime parsed;
|
|
|
|
if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) ||
|
|
DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed))
|
|
{
|
|
utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|