feat(backend): report context, fix deleting reports

This commit is contained in:
sam 2024-12-18 21:26:35 +01:00
parent bd21eeebcf
commit 546e900204
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
9 changed files with 133 additions and 3 deletions

View file

@ -18,6 +18,7 @@ using Foxnouns.Backend.Database.Models;
using Foxnouns.Backend.Dto; using Foxnouns.Backend.Dto;
using Foxnouns.Backend.Middleware; using Foxnouns.Backend.Middleware;
using Foxnouns.Backend.Services; using Foxnouns.Backend.Services;
using Foxnouns.Backend.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -49,6 +50,8 @@ public class ReportsController(
[FromBody] CreateReportRequest req [FromBody] CreateReportRequest req
) )
{ {
ValidationUtils.Validate([("context", ValidationUtils.ValidateReportContext(req.Context))]);
User target = await db.ResolveUserAsync(id); User target = await db.ResolveUserAsync(id);
if (target.Id == CurrentUser!.Id) if (target.Id == CurrentUser!.Id)
@ -96,6 +99,7 @@ public class ReportsController(
TargetUserId = target.Id, TargetUserId = target.Id,
TargetMemberId = null, TargetMemberId = null,
Reason = req.Reason, Reason = req.Reason,
Context = req.Context,
TargetType = ReportTargetType.User, TargetType = ReportTargetType.User,
TargetSnapshot = snapshot, TargetSnapshot = snapshot,
}; };
@ -112,6 +116,8 @@ public class ReportsController(
[FromBody] CreateReportRequest req [FromBody] CreateReportRequest req
) )
{ {
ValidationUtils.Validate([("context", ValidationUtils.ValidateReportContext(req.Context))]);
Member target = await db.ResolveMemberAsync(id); Member target = await db.ResolveMemberAsync(id);
if (target.User.Id == CurrentUser!.Id) if (target.User.Id == CurrentUser!.Id)
@ -158,6 +164,7 @@ public class ReportsController(
TargetUserId = target.User.Id, TargetUserId = target.User.Id,
TargetMemberId = target.Id, TargetMemberId = target.Id,
Reason = req.Reason, Reason = req.Reason,
Context = req.Context,
TargetType = ReportTargetType.Member, TargetType = ReportTargetType.Member,
TargetSnapshot = snapshot, TargetSnapshot = snapshot,
}; };

View file

@ -108,6 +108,12 @@ public class DatabaseContext(DbContextOptions options) : DbContext(options)
.HasFilter("fediverse_application_id IS NULL") .HasFilter("fediverse_application_id IS NULL")
.IsUnique(); .IsUnique();
modelBuilder
.Entity<AuditLogEntry>()
.HasOne(e => e.Report)
.WithOne(e => e.AuditLogEntry)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<User>().Property(u => u.Sid).HasDefaultValueSql("find_free_user_sid()"); modelBuilder.Entity<User>().Property(u => u.Sid).HasDefaultValueSql("find_free_user_sid()");
modelBuilder.Entity<User>().Property(u => u.Fields).HasColumnType("jsonb"); modelBuilder.Entity<User>().Property(u => u.Fields).HasColumnType("jsonb");
modelBuilder.Entity<User>().Property(u => u.Names).HasColumnType("jsonb"); modelBuilder.Entity<User>().Property(u => u.Names).HasColumnType("jsonb");

View file

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Foxnouns.Backend.Database.Migrations
{
/// <inheritdoc />
[DbContext(typeof(DatabaseContext))]
[Migration("20241218195457_AddContextToReports")]
public partial class AddContextToReports : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "context",
table: "reports",
type: "text",
nullable: true
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(name: "context", table: "reports");
}
}
}

View file

@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Foxnouns.Backend.Database.Migrations
{
/// <inheritdoc />
[DbContext(typeof(DatabaseContext))]
[Migration("20241218201855_MakeAuditLogReportsNullable")]
public partial class MakeAuditLogReportsNullable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_audit_log_reports_report_id",
table: "audit_log"
);
migrationBuilder.DropIndex(name: "ix_audit_log_report_id", table: "audit_log");
migrationBuilder.CreateIndex(
name: "ix_audit_log_report_id",
table: "audit_log",
column: "report_id",
unique: true
);
migrationBuilder.AddForeignKey(
name: "fk_audit_log_reports_report_id",
table: "audit_log",
column: "report_id",
principalTable: "reports",
principalColumn: "id",
onDelete: ReferentialAction.SetNull
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_audit_log_reports_report_id",
table: "audit_log"
);
migrationBuilder.DropIndex(name: "ix_audit_log_report_id", table: "audit_log");
migrationBuilder.CreateIndex(
name: "ix_audit_log_report_id",
table: "audit_log",
column: "report_id"
);
migrationBuilder.AddForeignKey(
name: "fk_audit_log_reports_report_id",
table: "audit_log",
column: "report_id",
principalTable: "reports",
principalColumn: "id"
);
}
}
}

View file

@ -113,6 +113,7 @@ namespace Foxnouns.Backend.Database.Migrations
.HasName("pk_audit_log"); .HasName("pk_audit_log");
b.HasIndex("ReportId") b.HasIndex("ReportId")
.IsUnique()
.HasDatabaseName("ix_audit_log_report_id"); .HasDatabaseName("ix_audit_log_report_id");
b.ToTable("audit_log", (string)null); b.ToTable("audit_log", (string)null);
@ -409,6 +410,10 @@ namespace Foxnouns.Backend.Database.Migrations
.HasColumnType("bigint") .HasColumnType("bigint")
.HasColumnName("id"); .HasColumnName("id");
b.Property<string>("Context")
.HasColumnType("text")
.HasColumnName("context");
b.Property<int>("Reason") b.Property<int>("Reason")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("reason"); .HasColumnName("reason");
@ -675,8 +680,9 @@ namespace Foxnouns.Backend.Database.Migrations
modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuditLogEntry", b => modelBuilder.Entity("Foxnouns.Backend.Database.Models.AuditLogEntry", b =>
{ {
b.HasOne("Foxnouns.Backend.Database.Models.Report", "Report") b.HasOne("Foxnouns.Backend.Database.Models.Report", "Report")
.WithMany() .WithOne("AuditLogEntry")
.HasForeignKey("ReportId") .HasForeignKey("Foxnouns.Backend.Database.Models.AuditLogEntry", "ReportId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_audit_log_reports_report_id"); .HasConstraintName("fk_audit_log_reports_report_id");
b.Navigation("Report"); b.Navigation("Report");
@ -839,6 +845,11 @@ namespace Foxnouns.Backend.Database.Migrations
b.Navigation("ProfileFlags"); b.Navigation("ProfileFlags");
}); });
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Report", b =>
{
b.Navigation("AuditLogEntry");
});
modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b => modelBuilder.Entity("Foxnouns.Backend.Database.Models.User", b =>
{ {
b.Navigation("AuthMethods"); b.Navigation("AuthMethods");

View file

@ -12,6 +12,7 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.ComponentModel.DataAnnotations.Schema;
using Foxnouns.Backend.Utils; using Foxnouns.Backend.Utils;
using Newtonsoft.Json; using Newtonsoft.Json;

View file

@ -29,9 +29,12 @@ public class Report : BaseModel
public ReportStatus Status { get; set; } public ReportStatus Status { get; set; }
public ReportReason Reason { get; init; } public ReportReason Reason { get; init; }
public string? Context { get; init; }
public ReportTargetType TargetType { get; init; } public ReportTargetType TargetType { get; init; }
public string? TargetSnapshot { get; init; } public string? TargetSnapshot { get; init; }
public AuditLogEntry? AuditLogEntry { get; set; }
} }
[JsonConverter(typeof(ScreamingSnakeCaseEnumConverter))] [JsonConverter(typeof(ScreamingSnakeCaseEnumConverter))]

View file

@ -57,7 +57,7 @@ public record NotificationResponse(
public record AuditLogEntity(Snowflake Id, string Username); public record AuditLogEntity(Snowflake Id, string Username);
public record CreateReportRequest(ReportReason Reason); public record CreateReportRequest(ReportReason Reason, string? Context = null);
public record IgnoreReportRequest(string? Reason = null); public record IgnoreReportRequest(string? Reason = null);

View file

@ -196,6 +196,13 @@ public static partial class ValidationUtils
}; };
} }
public const int MaximumReportContextLength = 512;
public static ValidationError? ValidateReportContext(string? context) =>
context?.Length > MaximumReportContextLength
? ValidationError.GenericValidationError("Avatar is too large", null)
: null;
public const int MinimumPasswordLength = 12; public const int MinimumPasswordLength = 12;
public const int MaximumPasswordLength = 1024; public const int MaximumPasswordLength = 1024;