summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Media.cs1
-rw-r--r--Migrations/20230905034704_ChecksumVerified.Designer.cs313
-rw-r--r--Migrations/20230905034704_ChecksumVerified.cs29
-rw-r--r--Migrations/HBContextModelSnapshot.cs3
-rw-r--r--Pages/ViewMedia.razor15
-rw-r--r--Pages/ViewMedia.razor.css4
-rw-r--r--Services/MediaService.cs1
-rw-r--r--Todo.md1
-rw-r--r--wwwroot/styles/global.css1
9 files changed, 364 insertions, 4 deletions
diff --git a/Media.cs b/Media.cs
index 2a4dab6..db38e9f 100644
--- a/Media.cs
+++ b/Media.cs
@@ -34,6 +34,7 @@ public class Media : HBObject {
public class UploadedFile : HBObject {
public string OriginalChecksum { get; set; }
+ public bool ChecksumVerified { get; set; } = false;
public string? Filename { get; set; }
public long Length { get; set; }
public DateTime UploadTime { get; set; } = DateTime.UtcNow;
diff --git a/Migrations/20230905034704_ChecksumVerified.Designer.cs b/Migrations/20230905034704_ChecksumVerified.Designer.cs
new file mode 100644
index 0000000..076816f
--- /dev/null
+++ b/Migrations/20230905034704_ChecksumVerified.Designer.cs
@@ -0,0 +1,313 @@
+// <auto-generated />
+using System;
+using HyperBooru;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ [DbContext(typeof(HBContext))]
+ [Migration("20230905034704_ChecksumVerified")]
+ partial class ChecksumVerified
+ {
+ /// <inheritdoc />
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Property<int>("ObjectId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ObjectId"));
+
+ b.Property<Guid>("Guid")
+ .HasColumnType("uuid");
+
+ b.HasKey("ObjectId");
+
+ b.HasIndex("Guid");
+
+ b.ToTable("Objects", (string)null);
+
+ b.UseTptMappingStrategy();
+ });
+
+ modelBuilder.Entity("HyperBooru.OcrData", b =>
+ {
+ b.Property<int>("OcrDataId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("OcrDataId"));
+
+ b.Property<int>("MediaId")
+ .HasColumnType("integer");
+
+ b.Property<string>("SearchableText")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("OcrDataId");
+
+ b.HasIndex("MediaId")
+ .IsUnique();
+
+ b.ToTable("OcrData");
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.Property<int>("ImplicitTagsObjectId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TagDefinitionObjectId")
+ .HasColumnType("integer");
+
+ b.HasKey("ImplicitTagsObjectId", "TagDefinitionObjectId");
+
+ b.HasIndex("TagDefinitionObjectId");
+
+ b.ToTable("TagDefinitionTagDefinition");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Checksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<int>("Height")
+ .HasColumnType("integer");
+
+ b.Property<string>("LongDescription")
+ .HasColumnType("text");
+
+ b.Property<string>("MimeType")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("ShortDescription")
+ .HasColumnType("text");
+
+ b.Property<int>("Width")
+ .HasColumnType("integer");
+
+ b.ToTable("Media", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<DateTime>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<int>("TagDefinitionId")
+ .HasColumnType("integer");
+
+ b.Property<int>("TargetObjectId")
+ .HasColumnType("integer");
+
+ b.HasIndex("TagDefinitionId");
+
+ b.HasIndex("TargetObjectId");
+
+ b.ToTable("Tags", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<string>("Alias")
+ .HasColumnType("text");
+
+ b.Property<string>("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<string>("Namespace")
+ .HasColumnType("text");
+
+ b.Property<int>("Source")
+ .HasColumnType("integer");
+
+ b.ToTable("TagDefinitions", (string)null);
+
+ b.HasData(
+ new
+ {
+ ObjectId = -1,
+ Guid = new Guid("ebdad4f8-455a-4351-8017-1d4854d6fa38"),
+ Name = "nsfw",
+ Source = 0
+ },
+ new
+ {
+ ObjectId = -2,
+ Guid = new Guid("ea212801-5bcc-4c0e-814f-fb9d30db58bc"),
+ Name = "ingest",
+ Source = 0
+ });
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.HasBaseType("HyperBooru.HBObject");
+
+ b.Property<bool>("ChecksumVerified")
+ .HasColumnType("boolean");
+
+ b.Property<DateTime?>("CreateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<string>("Filename")
+ .HasColumnType("text");
+
+ b.Property<DateTime?>("LastAccessTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<DateTime?>("LastWriteTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property<long>("Length")
+ .HasColumnType("bigint");
+
+ b.Property<int>("MediaObjectId")
+ .HasColumnType("integer");
+
+ b.Property<string>("OriginalChecksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("UploadTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasIndex("MediaObjectId");
+
+ b.ToTable("UploadedFiles", (string)null);
+ });
+
+ modelBuilder.Entity("HyperBooru.OcrData", b =>
+ {
+ b.HasOne("HyperBooru.Media", "Media")
+ .WithOne("OcrData")
+ .HasForeignKey("HyperBooru.OcrData", "MediaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Media");
+ });
+
+ modelBuilder.Entity("TagDefinitionTagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("ImplicitTagsObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", null)
+ .WithMany()
+ .HasForeignKey("TagDefinitionObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Media", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.Tag", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.Tag", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.TagDefinition", "TagDefinition")
+ .WithMany()
+ .HasForeignKey("TagDefinitionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", "Target")
+ .WithMany("Tags")
+ .HasForeignKey("TargetObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("TagDefinition");
+
+ b.Navigation("Target");
+ });
+
+ modelBuilder.Entity("HyperBooru.TagDefinition", b =>
+ {
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.TagDefinition", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("HyperBooru.UploadedFile", b =>
+ {
+ b.HasOne("HyperBooru.Media", "Media")
+ .WithMany("UploadedFiles")
+ .HasForeignKey("MediaObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("HyperBooru.HBObject", null)
+ .WithOne()
+ .HasForeignKey("HyperBooru.UploadedFile", "ObjectId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Media");
+ });
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Navigation("Tags");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.Navigation("OcrData");
+
+ b.Navigation("UploadedFiles");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20230905034704_ChecksumVerified.cs b/Migrations/20230905034704_ChecksumVerified.cs
new file mode 100644
index 0000000..8ac1f58
--- /dev/null
+++ b/Migrations/20230905034704_ChecksumVerified.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ /// <inheritdoc />
+ public partial class ChecksumVerified : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn<bool>(
+ name: "ChecksumVerified",
+ table: "UploadedFiles",
+ type: "boolean",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "ChecksumVerified",
+ table: "UploadedFiles");
+ }
+ }
+}
diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs
index 72b662f..3211e1f 100644
--- a/Migrations/HBContextModelSnapshot.cs
+++ b/Migrations/HBContextModelSnapshot.cs
@@ -174,6 +174,9 @@ namespace HyperBooru.Migrations
{
b.HasBaseType("HyperBooru.HBObject");
+ b.Property<bool>("ChecksumVerified")
+ .HasColumnType("boolean");
+
b.Property<DateTime?>("CreateTime")
.HasColumnType("timestamp with time zone");
diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor
index f92111b..91f33e6 100644
--- a/Pages/ViewMedia.razor
+++ b/Pages/ViewMedia.razor
@@ -56,12 +56,21 @@
</tr>
@foreach(var file in media.UploadedFiles) {
<tr>
- <td title=@file.CreateTime?.ToString()>@(file.CreateTime?.ToString("d") ?? "N/A")</td>
- <td title=@file.LastWriteTime?.ToString()>@(file.LastWriteTime?.ToString("d") ?? "N/A")</td>
+ <td title=@file.CreateTime?.ToString()>
+ @(file.CreateTime?.ToString("d") ?? "N/A")
+ </td>
+ <td title=@file.LastWriteTime?.ToString()>
+ @(file.LastWriteTime?.ToString("d") ?? "N/A")
+ </td>
<td title=@file.UploadTime>@(file.UploadTime.ToString("d"))</td>
<td title=@file.Filename>@file.Filename</td>
<td title=@file.Length>@file.Length.ToBytesSI()</td>
- <td title=@file.OriginalChecksum>@file.OriginalChecksum.Substring(0, 8)</td>
+ <td
+ title=@(file.OriginalChecksum + (file.ChecksumVerified ? " (verified)" : ""))
+ class=@(file.ChecksumVerified ? "verified" : null)>
+
+ @file.OriginalChecksum.Substring(0, 8)
+ </td>
</tr>
}
</table>
diff --git a/Pages/ViewMedia.razor.css b/Pages/ViewMedia.razor.css
index 16f86ec..53d5eca 100644
--- a/Pages/ViewMedia.razor.css
+++ b/Pages/ViewMedia.razor.css
@@ -103,6 +103,10 @@ table#uploaded-files td:nth-child(4) {
max-width: 170px;
}
+table#uploaded-files td.verified {
+ color: var(--col-checksum-verified-pri);
+}
+
p.heading {
margin-top: 30px;
}
diff --git a/Services/MediaService.cs b/Services/MediaService.cs
index ccc3283..015eae9 100644
--- a/Services/MediaService.cs
+++ b/Services/MediaService.cs
@@ -121,6 +121,7 @@ public class MediaService : IMediaService {
Filename = fileName,
Length = fileData.Length,
OriginalChecksum = hash,
+ ChecksumVerified = checksum is not null,
UploadTime = DateTime.UtcNow,
LastAccessTime = lastAccessTime,
LastWriteTime = lastWriteTime,
diff --git a/Todo.md b/Todo.md
index 4d4bb3f..a196f6d 100644
--- a/Todo.md
+++ b/Todo.md
@@ -26,7 +26,6 @@
- OCR status reporting on admin page
- Dynamically update OCR data on ViewMedia page
- Image deduplication by visual similarity
- - Rating system
- Audit log
- Journaled operations
- Confirmation dialog before enabling NSFW mode
diff --git a/wwwroot/styles/global.css b/wwwroot/styles/global.css
index a8c4202..6c1df53 100644
--- a/wwwroot/styles/global.css
+++ b/wwwroot/styles/global.css
@@ -4,6 +4,7 @@
--col-accent-pri: #0aa;
--col-accent-pri-hl: #0cc;
--col-error-pri: #ffaa00;
+ --col-checksum-verified-pri: #8dff76;
--col-bg: #222;
--col-dialog-bg: #333;
--col-navbar-bg: var(--col-accent-pri);