Create Audits on Actions

This commit is contained in:
Andre Beging
2022-05-23 11:07:52 +02:00
parent 5d713db83f
commit b16951a1a6
11 changed files with 358 additions and 5 deletions

View File

@@ -0,0 +1,40 @@
using FoodsharingSiegen.Contracts.Entity;
namespace FoodsharingSiegen.Server.Data
{
public static class AuditHelper
{
public static string CreateText(Audit audit)
{
switch (audit.Type)
{
case AuditType.SaveProfile:
return "hat das eigene Profil gespeichert.";
case AuditType.SetOwnPassword:
return "hat das eigene Passwort geändert.";
case AuditType.CreateUser:
return $"hat den User {audit.Data1} erstellt.";
case AuditType.UpdateUser:
return $"hat den User {audit.Data1} gespeichert.";
case AuditType.RemoveUser:
return $"hat den User {audit.Data1} gelöscht.";
case AuditType.SetUserPassword:
return $"hat das Passwort von {audit.Data1} geändert.";
case AuditType.CreateProspect:
return $"hat den Neuling {audit.Data1} erstellt.";
case AuditType.EditProspect:
return $"hat den Neuling {audit.Data1} bearbeitet.";
case AuditType.AddInteraction:
return $"hat dem Neuling {audit.Data1} folgendes hinzugefügt: {audit.Data2}";
case AuditType.RemoveInteraction:
return $"hat eine Interaktion bei {audit.Data1} gelöscht.";
break;
case AuditType.None:
default:
return $"{audit.Data1}, {audit.Data2}";
}
return string.Empty;
}
}
}

View File

@@ -1,6 +1,7 @@
using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Server.Auth;
using Microsoft.EntityFrameworkCore;
namespace FoodsharingSiegen.Server.Data.Service
{
@@ -36,6 +37,7 @@ namespace FoodsharingSiegen.Server.Data.Service
{
var audit = new Audit
{
Created = DateTime.Now,
Type = type,
UserID = CurrentUser?.Id,
Data1 = data1,
@@ -66,11 +68,11 @@ namespace FoodsharingSiegen.Server.Data.Service
/// <param name="count">The count</param>
/// <param name="type">The type</param>
/// <returns>A task containing an operation result of list audit</returns>
public async Task<OperationResult<List<Audit>>> Load(int count, AuditType? type)
public async Task<OperationResult<List<Audit>>> Load(int count, AuditType? type = null)
{
try
{
var query = Context.Audits?.OrderBy(x => x.Created).AsQueryable();
var query = Context.Audits?.Include(x => x.User).OrderByDescending(x => x.Created).AsQueryable();
if (count > 0)
query = query?.Take(count);

View File

@@ -46,6 +46,8 @@ namespace FoodsharingSiegen.Server.Data.Service
await Context.SaveChangesAsync();
await AuditService.Insert(AuditType.AddInteraction, targetProspect.Name, interaction.Type.ToString());
// Detatch entities
Context.Entry(targetProspect).State = EntityState.Detached;
Context.Entry(interaction).State = EntityState.Detached;
@@ -130,6 +132,8 @@ namespace FoodsharingSiegen.Server.Data.Service
Context.Interactions.Remove(new Interaction { Id = interactionId });
await Context.SaveChangesAsync();
await AuditService.Insert(AuditType.RemoveInteraction, "?");
return new OperationResult();
}
catch (Exception e)
@@ -161,6 +165,8 @@ namespace FoodsharingSiegen.Server.Data.Service
var saveR = await Context.SaveChangesAsync();
if(saveR < 1) return new OperationResult(new Exception("Fehler beim speichern"));
await AuditService.Insert(AuditType.EditProspect, prospect.Name);
return new OperationResult();
}
catch (Exception e)

View File

@@ -0,0 +1,195 @@
// <auto-generated />
using System;
using FoodsharingSiegen.Server.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace FoodsharingSiegen.Server.Migrations
{
[DbContext(typeof(FsContext))]
[Migration("20220523083959_AuditDate")]
partial class AuditDate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.1");
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Audit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("Data1")
.HasColumnType("TEXT");
b.Property<string>("Data2")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<Guid?>("UserID")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserID");
b.ToTable("Audits");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Interaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("Alert")
.HasColumnType("INTEGER");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<DateTime>("Date")
.HasColumnType("TEXT");
b.Property<string>("Info")
.HasColumnType("TEXT");
b.Property<bool>("NotNeeded")
.HasColumnType("INTEGER");
b.Property<Guid>("ProspectID")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<Guid>("UserID")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProspectID");
b.HasIndex("UserID");
b.ToTable("Interactions");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Prospect", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("FsId")
.HasColumnType("INTEGER");
b.Property<string>("Memo")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Prospects");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("EncryptedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("ForceLogout")
.HasColumnType("INTEGER");
b.Property<string>("Groups")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Mail")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Memo")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<bool>("Verified")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Audit", b =>
{
b.HasOne("FoodsharingSiegen.Contracts.Entity.User", "User")
.WithMany()
.HasForeignKey("UserID");
b.Navigation("User");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Interaction", b =>
{
b.HasOne("FoodsharingSiegen.Contracts.Entity.Prospect", "Prospect")
.WithMany("Interactions")
.HasForeignKey("ProspectID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("FoodsharingSiegen.Contracts.Entity.User", "User")
.WithMany("Interactions")
.HasForeignKey("UserID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Prospect");
b.Navigation("User");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Prospect", b =>
{
b.Navigation("Interactions");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.User", b =>
{
b.Navigation("Interactions");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FoodsharingSiegen.Server.Migrations
{
public partial class AuditDate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "Created",
table: "Audits",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldNullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "Created",
table: "Audits",
type: "TEXT",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "TEXT");
}
}
}

View File

@@ -23,7 +23,7 @@ namespace FoodsharingSiegen.Server.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime?>("Created")
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("Data1")

View File

@@ -0,0 +1,25 @@
@page "/audit"
@using FoodsharingSiegen.Server.BaseClasses
@using FoodsharingSiegen.Contracts.Entity
@using FoodsharingSiegen.Server.Data
@using FoodsharingSiegen.Server.Data.Service
@inherits FsBase
<PageTitle>Aktivitäten</PageTitle>
<h4>Aktivitäten</h4>
<DataGrid TItem="Audit"
Data="@Audits"
Responsive>
<DataGridColumn Field="@nameof(Audit.Created)" Caption="Datum" Width="180px" />
<DataGridColumn Caption="Aktion">
<DisplayTemplate>
<span>
@((context as Audit).User?.Name) @(AuditHelper.CreateText(context))
</span>
</DisplayTemplate>
</DataGridColumn>
</DataGrid>

View File

@@ -0,0 +1,46 @@
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Server.Data.Service;
using Microsoft.AspNetCore.Components;
namespace FoodsharingSiegen.Server.Pages
{
/// <summary>
/// The audit view class (a. beging, 23.05.2022)
/// </summary>
public partial class AuditView
{
#region Dependencies (Injected)
/// <summary>
/// Gets or sets the value of the audit service (ab)
/// </summary>
[Inject] public AuditService? AuditService { get; set; }
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the value of the audits (ab)
/// </summary>
private List<Audit>? Audits { get; set; }
#endregion
#region Override OnInitializedAsync
/// <summary>
/// Ons the initialized (a. beging, 23.05.2022)
/// </summary>
protected override async Task OnInitializedAsync()
{
var loadR = await AuditService?.Load(100)!;
if (loadR.Success)
Audits = loadR.Data;
await base.OnInitializedAsync();
}
#endregion
}
}

View File

@@ -9,7 +9,7 @@
<div style="width: 100%; max-width: 500px;">
<h4>Mein Profil</h4>
<Button Color="Color.Primary" Clicked="SaveProfile">Speichern</Button>
<Fields Class="mt-1">
<Fields Class="my-3">
<Validations @ref="ValidationsRef">
<Validation Validator="ValidationRule.IsNotEmpty">
<Field ColumnSize="ColumnSize.Is12">

View File

@@ -24,6 +24,11 @@
<span class="fas fa-users mr-1" aria-hidden="true" style="font-size: 1.4em;"></span> Benutzer
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="audit" Match="NavLinkMatch.All">
<span class="fa-solid fa-clock-rotate-left mr-1" aria-hidden="true" style="font-size: 1.4em;"></span> Aktivitäten
</NavLink>
</div>
<div class="nav-item px-3 pt-5">
<NavLink class="nav-link" href="logout" Match="NavLinkMatch.All">
<span class="fa-solid fa-door-open mr-1" aria-hidden="true" style="font-size: 1.4em;"></span> Ausloggen