summaryrefslogtreecommitdiffstats
path: root/api/WhatApi/Database/AppDatabase.cs
diff options
context:
space:
mode:
Diffstat (limited to 'api/WhatApi/Database/AppDatabase.cs')
-rw-r--r--api/WhatApi/Database/AppDatabase.cs113
1 files changed, 113 insertions, 0 deletions
diff --git a/api/WhatApi/Database/AppDatabase.cs b/api/WhatApi/Database/AppDatabase.cs
new file mode 100644
index 0000000..64c138f
--- /dev/null
+++ b/api/WhatApi/Database/AppDatabase.cs
@@ -0,0 +1,113 @@
+using System.Security.Claims;
+
+namespace WhatApi.Database;
+
+public class AppDatabase(DbContextOptions<AppDatabase> options, IHttpContextAccessor httpContextAccessor, IConfiguration configuration) : DbContext(options)
+{
+ public DbSet<Content> Content => Set<Content>();
+ public DbSet<Place> Places => Set<Place>();
+ public DbSet<User> Users => Set<User>();
+ public DbSet<AuditTrail> AuditTrails => Set<AuditTrail>();
+
+ protected override void OnModelCreating(ModelBuilder b) {
+ b.HasPostgresExtension("postgis");
+ b.ApplyConfiguration(new AuditTrailConfiguration());
+ b.ApplyConfiguration(new PlaceConfiguration());
+ b.ApplyConfiguration(new UserConfiguration());
+ b.ApplyConfiguration(new ContentConfiguration());
+ base.OnModelCreating(b);
+ }
+
+ public override int SaveChanges() {
+ if (configuration.GetValue<bool>("DISABLE_AUDIT_TRAILS")) return base.SaveChanges();
+ SetAuditableProperties();
+ var auditEntries = GetActiveAuditTrails();
+ if (auditEntries.Count != 0)
+ AuditTrails.AddRange(auditEntries);
+ return base.SaveChanges();
+ }
+
+ public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) {
+ if (configuration.GetValue<bool>("DISABLE_AUDIT_TRAILS")) return await base.SaveChangesAsync(cancellationToken);
+ SetAuditableProperties();
+ var auditEntries = GetActiveAuditTrails();
+ if (auditEntries.Count != 0)
+ await AuditTrails.AddRangeAsync(auditEntries, cancellationToken);
+ return await base.SaveChangesAsync(cancellationToken);
+ }
+
+ private List<AuditTrail> GetActiveAuditTrails() {
+ var userId = GetUserId();
+ var entries = ChangeTracker.Entries<IAuditableEntity>()
+ .Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted);
+
+ var auditTrails = new List<AuditTrail>();
+
+ foreach (var entry in entries) {
+ var audit = new AuditTrail {
+ Id = Guid.NewGuid(),
+ UserId = userId,
+ EntityName = entry.Entity.GetType().Name,
+ DateUtc = DateTimeOffset.UtcNow
+ };
+
+ foreach (var prop in entry.Properties) {
+ if (prop.Metadata.IsPrimaryKey()) {
+ audit.PrimaryKey = prop.CurrentValue?.ToString();
+ continue;
+ }
+
+ if (prop.Metadata.PropertyInfo?.CustomAttributes.FirstOrDefault(c => c.AttributeType == typeof(AuditTrailIgnoreAttribute)) != null)
+ continue;
+
+ var name = prop.Metadata.Name;
+
+ switch (entry.State) {
+ case EntityState.Added:
+ audit.TrailType = TrailType.Create;
+ audit.NewValues[name] = prop.CurrentValue;
+ break;
+ case EntityState.Deleted:
+ audit.TrailType = TrailType.Delete;
+ audit.OldValues[name] = prop.OriginalValue;
+ break;
+ case EntityState.Modified:
+ if (!Equals(prop.OriginalValue, prop.CurrentValue)) {
+ audit.TrailType = TrailType.Update;
+ audit.ChangedColumns.Add(name);
+ audit.OldValues[name] = prop.OriginalValue;
+ audit.NewValues[name] = prop.CurrentValue;
+ }
+ break;
+ }
+ }
+
+ if (audit.TrailType != TrailType.None)
+ auditTrails.Add(audit);
+ }
+
+ return auditTrails;
+ }
+
+ private Guid GetUserId() {
+ var system = new Guid("e87ab078-55bc-4655-86d9-c5b2ecad7162");
+ var userIdString = httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
+ return string.IsNullOrWhiteSpace(userIdString) ? system : new Guid(userIdString);
+ }
+
+ private void SetAuditableProperties() {
+ var actor = GetUserId();
+ foreach (var entry in ChangeTracker.Entries<IAuditableEntity>()) {
+ switch (entry.State) {
+ case EntityState.Added:
+ entry.Entity.CreatedAtUtc = DateTimeOffset.UtcNow;
+ entry.Entity.CreatedBy = actor;
+ break;
+ case EntityState.Modified:
+ entry.Entity.UpdatedAtUtc = DateTimeOffset.UtcNow;
+ entry.Entity.UpdatedBy = actor;
+ break;
+ }
+ }
+ }
+} \ No newline at end of file