summaryrefslogtreecommitdiffstats
path: root/api/WhatApi/Database/AppDatabase.cs
blob: 64c138fdb39a6fbf857216515fd1087a88e8217b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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;
            }
        }
    }
}