Initial commit
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PowderCoating.Application.Interfaces;
|
||||
|
||||
namespace PowderCoating.Infrastructure.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Writes manual audit log entries via raw SQL, matching the approach used by
|
||||
/// <see cref="AuditInterceptor"/> so neither path can trigger an infinite loop
|
||||
/// through the EF change tracker.
|
||||
/// </summary>
|
||||
public class AuditService : IAuditService
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public AuditService(ApplicationDbContext db, IHttpContextAccessor http)
|
||||
{
|
||||
_db = db;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task LogAsync(string action, string entityType, string? description = null,
|
||||
object? details = null, string? entityId = null)
|
||||
{
|
||||
var (userId, userName, companyId, ip) = GetRequestContext();
|
||||
var newValues = details != null ? JsonSerializer.Serialize(details) : null;
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO AuditLogs
|
||||
(UserId, UserName, CompanyId, CompanyName, Action, EntityType, EntityId,
|
||||
EntityDescription, OldValues, NewValues, IpAddress, Timestamp)
|
||||
VALUES (@userId, @userName, @companyId, @companyName, @action, @entityType, @entityId,
|
||||
@entityDescription, NULL, @newValues, @ipAddress, @timestamp)
|
||||
""";
|
||||
|
||||
SqlParameter[] p =
|
||||
[
|
||||
new("@userId", SqlDbType.NVarChar, -1) { Value = (object?)userId ?? DBNull.Value },
|
||||
new("@userName", SqlDbType.NVarChar, -1) { Value = (object)userName },
|
||||
new("@companyId", SqlDbType.Int) { Value = (object?)companyId ?? DBNull.Value },
|
||||
new("@companyName", SqlDbType.NVarChar, -1) { Value = DBNull.Value },
|
||||
new("@action", SqlDbType.NVarChar, -1) { Value = (object)action },
|
||||
new("@entityType", SqlDbType.NVarChar, -1) { Value = (object)entityType },
|
||||
new("@entityId", SqlDbType.NVarChar, -1) { Value = (object?)entityId ?? DBNull.Value },
|
||||
new("@entityDescription", SqlDbType.NVarChar, -1) { Value = (object?)description ?? DBNull.Value },
|
||||
new("@newValues", SqlDbType.NVarChar, -1) { Value = (object?)newValues ?? DBNull.Value },
|
||||
new("@ipAddress", SqlDbType.NVarChar, -1) { Value = (object?)ip ?? DBNull.Value },
|
||||
new("@timestamp", SqlDbType.DateTime2) { Value = DateTime.UtcNow },
|
||||
];
|
||||
|
||||
await _db.Database.ExecuteSqlRawAsync(sql, p);
|
||||
}
|
||||
|
||||
private (string? userId, string userName, int? companyId, string? ip) GetRequestContext()
|
||||
{
|
||||
var ctx = _http.HttpContext;
|
||||
if (ctx == null) return (null, "System", null, null);
|
||||
|
||||
var user = ctx.User;
|
||||
var userId = user.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
var userName = user.Identity?.Name
|
||||
?? user.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value
|
||||
?? "Unknown";
|
||||
var cidStr = user.FindFirst("CompanyId")?.Value;
|
||||
int? companyId = cidStr != null && int.TryParse(cidStr, out var c) ? c : null;
|
||||
var ip = ctx.Connection.RemoteIpAddress?.ToString();
|
||||
|
||||
return (userId, userName, companyId, ip);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user