summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Controllers/MediaController.cs2
-rw-r--r--HBContext.cs10
-rw-r--r--Media.cs2
-rw-r--r--Migrations/20230814013743_Initial.Designer.cs234
-rw-r--r--Migrations/20230816233054_ImplicitTags.cs123
-rw-r--r--Migrations/20230817010522_Initial.Designer.cs (renamed from Migrations/20230816233054_ImplicitTags.Designer.cs)62
-rw-r--r--Migrations/20230817010522_Initial.cs (renamed from Migrations/20230814013743_Initial.cs)101
-rw-r--r--Migrations/HBContextModelSnapshot.cs58
-rw-r--r--Pages/TagDefinitions.razor8
-rw-r--r--Server.csproj2
-rw-r--r--Services/ConfigService.cs10
-rw-r--r--Services/TagService.cs35
-rw-r--r--Tag.cs10
-rw-r--r--appsettings.json3
14 files changed, 172 insertions, 488 deletions
diff --git a/Controllers/MediaController.cs b/Controllers/MediaController.cs
index 3f36064..3fd716e 100644
--- a/Controllers/MediaController.cs
+++ b/Controllers/MediaController.cs
@@ -119,7 +119,7 @@ public class MediaController : Controller {
var fileRecord = new UploadedFile() {
Filename = formFile.FileName,
OriginalChecksum = hash,
- UploadTime = DateTime.Now,
+ UploadTime = DateTime.UtcNow,
LastAccessTime = lastAccessTime,
LastWriteTime = lastWriteTime,
CreateTime = createTime
diff --git a/HBContext.cs b/HBContext.cs
index 162a2c7..04f2d1a 100644
--- a/HBContext.cs
+++ b/HBContext.cs
@@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using HyperBooru.Services;
-using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
namespace HyperBooru;
@@ -18,9 +17,7 @@ public class HBContext : DbContext {
protected override void OnConfiguring(DbContextOptionsBuilder options) {
options.UseLazyLoadingProxies();
-
- var path = Path.Join(config.DataPath, "HyperBooru.db");
- options.UseSqlite($"Data Source = {config.DbPath}");
+ options.UseNpgsql(config.DbConnectionString);
#if DEBUG
options.EnableSensitiveDataLogging();
@@ -28,17 +25,22 @@ public class HBContext : DbContext {
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
+ // Don't use shared tables for inherited types
modelBuilder.Entity<HBObject>().ToTable("Objects");
modelBuilder.Entity<TagDefinition>().ToTable("TagDefinitions");
modelBuilder.Entity<Tag>().ToTable("Tags");
modelBuilder.Entity<Media>().ToTable("Media");
modelBuilder.Entity<UploadedFile>().ToTable("UploadedFiles");
+ // Seed internal tag definitions
+ // These should NEVER change
modelBuilder.Entity<TagDefinition>().HasData(new TagDefinition[] {
new() { ObjectId = -1, Source = TagSource.Internal, Name = "nsfw" },
new() { ObjectId = -2, Source = TagSource.Internal, Name = "ingest" }
});
+ // Implicit tags need some special attention to make many<->many
+ // navigations work for the same object type.
modelBuilder.Entity<TagDefinition>()
.HasMany(e => e.ImplicitTags)
.WithMany();
diff --git a/Media.cs b/Media.cs
index 68ae4c4..2ff10bf 100644
--- a/Media.cs
+++ b/Media.cs
@@ -35,7 +35,7 @@ public record UploadedFile {
public int UploadedFileId { get; set; }
public string OriginalChecksum { get; set; }
public string? Filename { get; set; }
- public DateTime UploadTime { get; set; } = DateTime.Now;
+ public DateTime UploadTime { get; set; } = DateTime.UtcNow;
public DateTime? LastAccessTime { get; set; }
public DateTime? LastWriteTime { get; set; }
public DateTime? CreateTime { get; set; }
diff --git a/Migrations/20230814013743_Initial.Designer.cs b/Migrations/20230814013743_Initial.Designer.cs
deleted file mode 100644
index c221c85..0000000
--- a/Migrations/20230814013743_Initial.Designer.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-// <auto-generated />
-using System;
-using HyperBooru;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-#nullable disable
-
-namespace HyperBooru.Migrations
-{
- [DbContext(typeof(HBContext))]
- [Migration("20230814013743_Initial")]
- partial class Initial
- {
- /// <inheritdoc />
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "7.0.10")
- .HasAnnotation("Proxies:ChangeTracking", false)
- .HasAnnotation("Proxies:CheckEquality", false)
- .HasAnnotation("Proxies:LazyLoading", true);
-
- modelBuilder.Entity("HyperBooru.HBObject", b =>
- {
- b.Property<int>("ObjectId")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<Guid>("Guid")
- .HasColumnType("TEXT");
-
- b.HasKey("ObjectId");
-
- b.HasIndex("Guid");
-
- b.ToTable("Objects", (string)null);
-
- b.UseTptMappingStrategy();
- });
-
- modelBuilder.Entity("HyperBooru.UploadedFile", b =>
- {
- b.Property<int>("UploadedFileId")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property<DateTime?>("CreateTime")
- .HasColumnType("TEXT");
-
- b.Property<string>("Filename")
- .HasColumnType("TEXT");
-
- b.Property<DateTime?>("LastAccessTime")
- .HasColumnType("TEXT");
-
- b.Property<DateTime?>("LastWriteTime")
- .HasColumnType("TEXT");
-
- b.Property<int>("MediaObjectId")
- .HasColumnType("INTEGER");
-
- b.Property<string>("OriginalChecksum")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UploadTime")
- .HasColumnType("TEXT");
-
- b.HasKey("UploadedFileId");
-
- b.HasIndex("MediaObjectId");
-
- b.ToTable("UploadedFiles", (string)null);
- });
-
- modelBuilder.Entity("HyperBooru.Media", b =>
- {
- b.HasBaseType("HyperBooru.HBObject");
-
- b.Property<string>("Checksum")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<string>("LongDescription")
- .HasColumnType("TEXT");
-
- b.Property<string>("MimeType")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<string>("ShortDescription")
- .HasColumnType("TEXT");
-
- b.ToTable("Media", (string)null);
- });
-
- modelBuilder.Entity("HyperBooru.Tag", b =>
- {
- b.HasBaseType("HyperBooru.HBObject");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnType("TEXT");
-
- b.Property<int>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
-
- b.Property<int>("TargetObjectId")
- .HasColumnType("INTEGER");
-
- b.HasIndex("TagDefinitionObjectId");
-
- b.HasIndex("TargetObjectId");
-
- b.ToTable("Tags", (string)null);
- });
-
- modelBuilder.Entity("HyperBooru.TagDefinition", b =>
- {
- b.HasBaseType("HyperBooru.HBObject");
-
- b.Property<string>("Name")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property<string>("Namespace")
- .HasColumnType("TEXT");
-
- b.Property<int>("Source")
- .HasColumnType("INTEGER");
-
- b.Property<int?>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
-
- b.HasIndex("TagDefinitionObjectId");
-
- b.ToTable("TagDefinitions", (string)null);
-
- b.HasData(
- new
- {
- ObjectId = -1,
- Guid = new Guid("cbd871cb-8790-4519-9f74-90d1e13a48a0"),
- Name = "nsfw",
- Source = 0
- },
- new
- {
- ObjectId = -2,
- Guid = new Guid("716ac11f-135a-433c-84a7-7bdfddbec004"),
- Name = "ingest",
- Source = 0
- });
- });
-
- modelBuilder.Entity("HyperBooru.UploadedFile", b =>
- {
- b.HasOne("HyperBooru.Media", "Media")
- .WithMany("UploadedFiles")
- .HasForeignKey("MediaObjectId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.Navigation("Media");
- });
-
- 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("TagDefinitionObjectId")
- .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();
-
- b.HasOne("HyperBooru.TagDefinition", null)
- .WithMany("ImplicitTags")
- .HasForeignKey("TagDefinitionObjectId");
- });
-
- modelBuilder.Entity("HyperBooru.HBObject", b =>
- {
- b.Navigation("Tags");
- });
-
- modelBuilder.Entity("HyperBooru.Media", b =>
- {
- b.Navigation("UploadedFiles");
- });
-
- modelBuilder.Entity("HyperBooru.TagDefinition", b =>
- {
- b.Navigation("ImplicitTags");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Migrations/20230816233054_ImplicitTags.cs b/Migrations/20230816233054_ImplicitTags.cs
deleted file mode 100644
index 26078d6..0000000
--- a/Migrations/20230816233054_ImplicitTags.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace HyperBooru.Migrations
-{
- /// <inheritdoc />
- public partial class ImplicitTags : Migration
- {
- /// <inheritdoc />
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_TagDefinitions_TagDefinitions_TagDefinitionObjectId",
- table: "TagDefinitions");
-
- migrationBuilder.DropIndex(
- name: "IX_TagDefinitions_TagDefinitionObjectId",
- table: "TagDefinitions");
-
- migrationBuilder.DropColumn(
- name: "TagDefinitionObjectId",
- table: "TagDefinitions");
-
- migrationBuilder.CreateTable(
- name: "TagDefinitionTagDefinition",
- columns: table => new
- {
- ImplicitTagsObjectId = table.Column<int>(type: "INTEGER", nullable: false),
- TagDefinitionObjectId = table.Column<int>(type: "INTEGER", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_TagDefinitionTagDefinition", x => new { x.ImplicitTagsObjectId, x.TagDefinitionObjectId });
- table.ForeignKey(
- name: "FK_TagDefinitionTagDefinition_TagDefinitions_ImplicitTagsObjectId",
- column: x => x.ImplicitTagsObjectId,
- principalTable: "TagDefinitions",
- principalColumn: "ObjectId",
- onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_TagDefinitionTagDefinition_TagDefinitions_TagDefinitionObjectId",
- column: x => x.TagDefinitionObjectId,
- principalTable: "TagDefinitions",
- principalColumn: "ObjectId",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.UpdateData(
- table: "Objects",
- keyColumn: "ObjectId",
- keyValue: -2,
- column: "Guid",
- value: new Guid("8b9cd13c-422a-4958-b97e-b858ac9301db"));
-
- migrationBuilder.UpdateData(
- table: "Objects",
- keyColumn: "ObjectId",
- keyValue: -1,
- column: "Guid",
- value: new Guid("13399c38-34b8-44a1-ac42-536bd32de96a"));
-
- migrationBuilder.CreateIndex(
- name: "IX_TagDefinitionTagDefinition_TagDefinitionObjectId",
- table: "TagDefinitionTagDefinition",
- column: "TagDefinitionObjectId");
- }
-
- /// <inheritdoc />
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "TagDefinitionTagDefinition");
-
- migrationBuilder.AddColumn<int>(
- name: "TagDefinitionObjectId",
- table: "TagDefinitions",
- type: "INTEGER",
- nullable: true);
-
- migrationBuilder.UpdateData(
- table: "Objects",
- keyColumn: "ObjectId",
- keyValue: -2,
- column: "Guid",
- value: new Guid("716ac11f-135a-433c-84a7-7bdfddbec004"));
-
- migrationBuilder.UpdateData(
- table: "Objects",
- keyColumn: "ObjectId",
- keyValue: -1,
- column: "Guid",
- value: new Guid("cbd871cb-8790-4519-9f74-90d1e13a48a0"));
-
- migrationBuilder.UpdateData(
- table: "TagDefinitions",
- keyColumn: "ObjectId",
- keyValue: -2,
- column: "TagDefinitionObjectId",
- value: null);
-
- migrationBuilder.UpdateData(
- table: "TagDefinitions",
- keyColumn: "ObjectId",
- keyValue: -1,
- column: "TagDefinitionObjectId",
- value: null);
-
- migrationBuilder.CreateIndex(
- name: "IX_TagDefinitions_TagDefinitionObjectId",
- table: "TagDefinitions",
- column: "TagDefinitionObjectId");
-
- migrationBuilder.AddForeignKey(
- name: "FK_TagDefinitions_TagDefinitions_TagDefinitionObjectId",
- table: "TagDefinitions",
- column: "TagDefinitionObjectId",
- principalTable: "TagDefinitions",
- principalColumn: "ObjectId");
- }
- }
-}
diff --git a/Migrations/20230816233054_ImplicitTags.Designer.cs b/Migrations/20230817010522_Initial.Designer.cs
index 531b14c..1219beb 100644
--- a/Migrations/20230816233054_ImplicitTags.Designer.cs
+++ b/Migrations/20230817010522_Initial.Designer.cs
@@ -5,14 +5,15 @@ 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("20230816233054_ImplicitTags")]
- partial class ImplicitTags
+ [Migration("20230817010522_Initial")]
+ partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -22,16 +23,21 @@ namespace HyperBooru.Migrations
.HasAnnotation("ProductVersion", "7.0.10")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
- .HasAnnotation("Proxies:LazyLoading", true);
+ .HasAnnotation("Proxies:LazyLoading", true)
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("HyperBooru.HBObject", b =>
{
b.Property<int>("ObjectId")
.ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ObjectId"));
b.Property<Guid>("Guid")
- .HasColumnType("TEXT");
+ .HasColumnType("uuid");
b.HasKey("ObjectId");
@@ -46,29 +52,31 @@ namespace HyperBooru.Migrations
{
b.Property<int>("UploadedFileId")
.ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UploadedFileId"));
b.Property<DateTime?>("CreateTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<string>("Filename")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<DateTime?>("LastAccessTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<DateTime?>("LastWriteTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<int>("MediaObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<string>("OriginalChecksum")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<DateTime>("UploadTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.HasKey("UploadedFileId");
@@ -80,10 +88,10 @@ namespace HyperBooru.Migrations
modelBuilder.Entity("TagDefinitionTagDefinition", b =>
{
b.Property<int>("ImplicitTagsObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<int>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.HasKey("ImplicitTagsObjectId", "TagDefinitionObjectId");
@@ -98,17 +106,17 @@ namespace HyperBooru.Migrations
b.Property<string>("Checksum")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("LongDescription")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("MimeType")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("ShortDescription")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.ToTable("Media", (string)null);
});
@@ -118,13 +126,13 @@ namespace HyperBooru.Migrations
b.HasBaseType("HyperBooru.HBObject");
b.Property<DateTime>("CreateTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<int>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<int>("TargetObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.HasIndex("TagDefinitionObjectId");
@@ -139,13 +147,13 @@ namespace HyperBooru.Migrations
b.Property<string>("Name")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("Namespace")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<int>("Source")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.ToTable("TagDefinitions", (string)null);
@@ -153,14 +161,14 @@ namespace HyperBooru.Migrations
new
{
ObjectId = -1,
- Guid = new Guid("13399c38-34b8-44a1-ac42-536bd32de96a"),
+ Guid = new Guid("e906ff13-ee7d-4eb2-826b-1cf880590f6b"),
Name = "nsfw",
Source = 0
},
new
{
ObjectId = -2,
- Guid = new Guid("8b9cd13c-422a-4958-b97e-b858ac9301db"),
+ Guid = new Guid("bab0a0f2-b126-429c-a80e-4f339a1f4bb2"),
Name = "ingest",
Source = 0
});
diff --git a/Migrations/20230814013743_Initial.cs b/Migrations/20230817010522_Initial.cs
index 1390c18..c8c8221 100644
--- a/Migrations/20230814013743_Initial.cs
+++ b/Migrations/20230817010522_Initial.cs
@@ -1,5 +1,6 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
@@ -17,9 +18,9 @@ namespace HyperBooru.Migrations
name: "Objects",
columns: table => new
{
- ObjectId = table.Column<int>(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- Guid = table.Column<Guid>(type: "TEXT", nullable: false)
+ ObjectId = table.Column<int>(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ Guid = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
@@ -30,12 +31,11 @@ namespace HyperBooru.Migrations
name: "Media",
columns: table => new
{
- ObjectId = table.Column<int>(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- Checksum = table.Column<string>(type: "TEXT", nullable: false),
- MimeType = table.Column<string>(type: "TEXT", nullable: false),
- ShortDescription = table.Column<string>(type: "TEXT", nullable: true),
- LongDescription = table.Column<string>(type: "TEXT", nullable: true)
+ ObjectId = table.Column<int>(type: "integer", nullable: false),
+ Checksum = table.Column<string>(type: "text", nullable: false),
+ MimeType = table.Column<string>(type: "text", nullable: false),
+ ShortDescription = table.Column<string>(type: "text", nullable: true),
+ LongDescription = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
@@ -52,12 +52,10 @@ namespace HyperBooru.Migrations
name: "TagDefinitions",
columns: table => new
{
- ObjectId = table.Column<int>(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- Source = table.Column<int>(type: "INTEGER", nullable: false),
- Namespace = table.Column<string>(type: "TEXT", nullable: true),
- Name = table.Column<string>(type: "TEXT", nullable: false),
- TagDefinitionObjectId = table.Column<int>(type: "INTEGER", nullable: true)
+ ObjectId = table.Column<int>(type: "integer", nullable: false),
+ Source = table.Column<int>(type: "integer", nullable: false),
+ Namespace = table.Column<string>(type: "text", nullable: true),
+ Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
@@ -68,26 +66,21 @@ namespace HyperBooru.Migrations
principalTable: "Objects",
principalColumn: "ObjectId",
onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_TagDefinitions_TagDefinitions_TagDefinitionObjectId",
- column: x => x.TagDefinitionObjectId,
- principalTable: "TagDefinitions",
- principalColumn: "ObjectId");
});
migrationBuilder.CreateTable(
name: "UploadedFiles",
columns: table => new
{
- UploadedFileId = table.Column<int>(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- OriginalChecksum = table.Column<string>(type: "TEXT", nullable: false),
- Filename = table.Column<string>(type: "TEXT", nullable: true),
- UploadTime = table.Column<DateTime>(type: "TEXT", nullable: false),
- LastAccessTime = table.Column<DateTime>(type: "TEXT", nullable: true),
- LastWriteTime = table.Column<DateTime>(type: "TEXT", nullable: true),
- CreateTime = table.Column<DateTime>(type: "TEXT", nullable: true),
- MediaObjectId = table.Column<int>(type: "INTEGER", nullable: false)
+ UploadedFileId = table.Column<int>(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ OriginalChecksum = table.Column<string>(type: "text", nullable: false),
+ Filename = table.Column<string>(type: "text", nullable: true),
+ UploadTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
+ LastAccessTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
+ LastWriteTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
+ CreateTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
+ MediaObjectId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
@@ -101,14 +94,37 @@ namespace HyperBooru.Migrations
});
migrationBuilder.CreateTable(
+ name: "TagDefinitionTagDefinition",
+ columns: table => new
+ {
+ ImplicitTagsObjectId = table.Column<int>(type: "integer", nullable: false),
+ TagDefinitionObjectId = table.Column<int>(type: "integer", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_TagDefinitionTagDefinition", x => new { x.ImplicitTagsObjectId, x.TagDefinitionObjectId });
+ table.ForeignKey(
+ name: "FK_TagDefinitionTagDefinition_TagDefinitions_ImplicitTagsObjec~",
+ column: x => x.ImplicitTagsObjectId,
+ principalTable: "TagDefinitions",
+ principalColumn: "ObjectId",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_TagDefinitionTagDefinition_TagDefinitions_TagDefinitionObje~",
+ column: x => x.TagDefinitionObjectId,
+ principalTable: "TagDefinitions",
+ principalColumn: "ObjectId",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
- ObjectId = table.Column<int>(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- TagDefinitionObjectId = table.Column<int>(type: "INTEGER", nullable: false),
- CreateTime = table.Column<DateTime>(type: "TEXT", nullable: false),
- TargetObjectId = table.Column<int>(type: "INTEGER", nullable: false)
+ ObjectId = table.Column<int>(type: "integer", nullable: false),
+ TagDefinitionObjectId = table.Column<int>(type: "integer", nullable: false),
+ CreateTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
+ TargetObjectId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
@@ -138,17 +154,17 @@ namespace HyperBooru.Migrations
columns: new[] { "ObjectId", "Guid" },
values: new object[,]
{
- { -2, new Guid("716ac11f-135a-433c-84a7-7bdfddbec004") },
- { -1, new Guid("cbd871cb-8790-4519-9f74-90d1e13a48a0") }
+ { -2, new Guid("bab0a0f2-b126-429c-a80e-4f339a1f4bb2") },
+ { -1, new Guid("e906ff13-ee7d-4eb2-826b-1cf880590f6b") }
});
migrationBuilder.InsertData(
table: "TagDefinitions",
- columns: new[] { "ObjectId", "Name", "Namespace", "Source", "TagDefinitionObjectId" },
+ columns: new[] { "ObjectId", "Name", "Namespace", "Source" },
values: new object[,]
{
- { -2, "ingest", null, 0, null },
- { -1, "nsfw", null, 0, null }
+ { -2, "ingest", null, 0 },
+ { -1, "nsfw", null, 0 }
});
migrationBuilder.CreateIndex(
@@ -157,8 +173,8 @@ namespace HyperBooru.Migrations
column: "Guid");
migrationBuilder.CreateIndex(
- name: "IX_TagDefinitions_TagDefinitionObjectId",
- table: "TagDefinitions",
+ name: "IX_TagDefinitionTagDefinition_TagDefinitionObjectId",
+ table: "TagDefinitionTagDefinition",
column: "TagDefinitionObjectId");
migrationBuilder.CreateIndex(
@@ -181,6 +197,9 @@ namespace HyperBooru.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
+ name: "TagDefinitionTagDefinition");
+
+ migrationBuilder.DropTable(
name: "Tags");
migrationBuilder.DropTable(
diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs
index 06712a6..aac8471 100644
--- a/Migrations/HBContextModelSnapshot.cs
+++ b/Migrations/HBContextModelSnapshot.cs
@@ -4,6 +4,7 @@ using HyperBooru;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
@@ -19,16 +20,21 @@ namespace HyperBooru.Migrations
.HasAnnotation("ProductVersion", "7.0.10")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
- .HasAnnotation("Proxies:LazyLoading", true);
+ .HasAnnotation("Proxies:LazyLoading", true)
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("HyperBooru.HBObject", b =>
{
b.Property<int>("ObjectId")
.ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("ObjectId"));
b.Property<Guid>("Guid")
- .HasColumnType("TEXT");
+ .HasColumnType("uuid");
b.HasKey("ObjectId");
@@ -43,29 +49,31 @@ namespace HyperBooru.Migrations
{
b.Property<int>("UploadedFileId")
.ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UploadedFileId"));
b.Property<DateTime?>("CreateTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<string>("Filename")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<DateTime?>("LastAccessTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<DateTime?>("LastWriteTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<int>("MediaObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<string>("OriginalChecksum")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<DateTime>("UploadTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.HasKey("UploadedFileId");
@@ -77,10 +85,10 @@ namespace HyperBooru.Migrations
modelBuilder.Entity("TagDefinitionTagDefinition", b =>
{
b.Property<int>("ImplicitTagsObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<int>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.HasKey("ImplicitTagsObjectId", "TagDefinitionObjectId");
@@ -95,17 +103,17 @@ namespace HyperBooru.Migrations
b.Property<string>("Checksum")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("LongDescription")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("MimeType")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("ShortDescription")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.ToTable("Media", (string)null);
});
@@ -115,13 +123,13 @@ namespace HyperBooru.Migrations
b.HasBaseType("HyperBooru.HBObject");
b.Property<DateTime>("CreateTime")
- .HasColumnType("TEXT");
+ .HasColumnType("timestamp with time zone");
b.Property<int>("TagDefinitionObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.Property<int>("TargetObjectId")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.HasIndex("TagDefinitionObjectId");
@@ -136,13 +144,13 @@ namespace HyperBooru.Migrations
b.Property<string>("Name")
.IsRequired()
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<string>("Namespace")
- .HasColumnType("TEXT");
+ .HasColumnType("text");
b.Property<int>("Source")
- .HasColumnType("INTEGER");
+ .HasColumnType("integer");
b.ToTable("TagDefinitions", (string)null);
@@ -150,14 +158,14 @@ namespace HyperBooru.Migrations
new
{
ObjectId = -1,
- Guid = new Guid("13399c38-34b8-44a1-ac42-536bd32de96a"),
+ Guid = new Guid("e906ff13-ee7d-4eb2-826b-1cf880590f6b"),
Name = "nsfw",
Source = 0
},
new
{
ObjectId = -2,
- Guid = new Guid("8b9cd13c-422a-4958-b97e-b858ac9301db"),
+ Guid = new Guid("bab0a0f2-b126-429c-a80e-4f339a1f4bb2"),
Name = "ingest",
Source = 0
});
diff --git a/Pages/TagDefinitions.razor b/Pages/TagDefinitions.razor
index 75e93f9..cb0a17c 100644
--- a/Pages/TagDefinitions.razor
+++ b/Pages/TagDefinitions.razor
@@ -73,11 +73,12 @@
private TagDefinition? toDelete;
private TagDefinition? toEditImplicit;
- private IEnumerable<TagDefinition> tagDefinitions =>
+ private TagDefinition[] tagDefinitions =>
dbFactory.CreateDbContext().TagDefinitions
.Where(td => td.Source == TagSource.UserTag)
.OrderBy(td => td.Namespace)
- .ThenBy(td => td.Name);
+ .ThenBy(td => td.Name)
+ .ToArray();
private void CreateTagDefinition() {
if(string.IsNullOrEmpty(tagNamespace))
@@ -112,8 +113,7 @@
if(toEditImplicit is null)
return;
- foreach(var tagDef in tagDefs)
- tagService.AddImplicitTag(toEditImplicit, tagDef);
+ tagService.SetImplicitTags(toEditImplicit, tagDefs);
StateHasChanged();
}
}
diff --git a/Server.csproj b/Server.csproj
index df0cb5e..f762884 100644
--- a/Server.csproj
+++ b/Server.csproj
@@ -27,8 +27,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Mime-Detective" Version="23.6.1" />
+ <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs
index 6ba3936..814a47b 100644
--- a/Services/ConfigService.cs
+++ b/Services/ConfigService.cs
@@ -1,9 +1,9 @@
namespace HyperBooru.Services;
public interface IConfigService {
- public string DataPath { get; }
- public string DbPath { get; }
- public string MediaBasePath { get; }
+ public string DataPath { get; }
+ public string DbConnectionString { get; }
+ public string MediaBasePath { get; }
public string GetPath(Media media);
public string GetPath(Media media, int width, int height);
@@ -38,8 +38,8 @@ public class ConfigService : IConfigService {
}
}
- public string DbPath =>
- Path.Join(DataPath, $"{AppName}.db");
+ public string DbConnectionString =>
+ config.GetConnectionString("DefaultConnection");
public string MediaBasePath =>
Path.Join(DataPath, "media");
diff --git a/Services/TagService.cs b/Services/TagService.cs
index da2bb2d..c3b8e14 100644
--- a/Services/TagService.cs
+++ b/Services/TagService.cs
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.EntityFrameworkCore;
+using System.Reflection.Metadata;
namespace HyperBooru.Services;
@@ -9,8 +11,8 @@ public interface ITagService {
public void AddTag(HBObject obj, TagDefinition tagDef);
public void RemoveTag(Guid obj, Guid tagDef);
public void RemoveTag(HBObject obj, TagDefinition tagDef);
- public void AddImplicitTag(TagDefinition tagDef, TagDefinition implicitTagDef);
- public void RemoveImplicitTag(TagDefinition tagDef, TagDefinition implicitTagDef);
+ public void SetImplicitTags(TagDefinition tagDef, TagDefinition[] implicitTagDefs);
+ public void SetImplicitTags(Guid tagDef, Guid[] implicitTagDefs);
public void CreateTagDefinition(string name, string? @namespace);
public void DeleteTagDefinition(TagDefinition tagDef);
public (TagDefinition tagDefinition, bool isImplicit)[] GetAllTags(Guid obj);
@@ -52,28 +54,27 @@ public class TagService : ITagService {
public void RemoveTag(HBObject obj, TagDefinition tagDef) =>
RemoveTag(obj.Guid, tagDef.Guid);
- public void AddImplicitTag(TagDefinition tagDef, TagDefinition implicitTagDef) {
+ public void SetImplicitTags(Guid tagDef, Guid[] implicitTagDefs) {
using var db = dbFactory.CreateDbContext();
- db.Entry(tagDef).State = EntityState.Unchanged;
-
- if(tagDef.ImplicitTags.Select(td => td.Guid).Contains(implicitTagDef.Guid))
- throw new ArgumentException("Tag definition already contains implicit tag");
-
- tagDef.ImplicitTags.Add(implicitTagDef);
- db.SaveChanges();
- }
+ using var transaction = db.Database.BeginTransaction();
- public void RemoveImplicitTag(TagDefinition tagDef, TagDefinition implicitTagDef) {
- using var db = dbFactory.CreateDbContext();
- db.Entry(tagDef).State = EntityState.Unchanged;
+ var tag = db.TagDefinitions.First(td => td.Guid == tagDef);
- if(!tagDef.ImplicitTags.Select(td => td.Guid).Contains(implicitTagDef.Guid))
- throw new ArgumentException("Tag definition doesn't contain implicit tag");
+ tag.ImplicitTags.RemoveAll(td => !implicitTagDefs.Contains(td.Guid));
+ tag.ImplicitTags.AddRange(
+ db.TagDefinitions
+ .Where(td => implicitTagDefs.Contains(td.Guid))
+ .Where(td => !tag.ImplicitTags
+ .Select(td => td.Guid)
+ .Contains(td.Guid)));
- tagDef.ImplicitTags.Remove(implicitTagDef);
db.SaveChanges();
+ transaction.Commit();
}
+ public void SetImplicitTags(TagDefinition tagDef, TagDefinition[] implicitTagDefs) =>
+ SetImplicitTags(tagDef.Guid, implicitTagDefs.Select(td => td.Guid).ToArray());
+
public void CreateTagDefinition(string name, string? @namespace) {
using var db = dbFactory.CreateDbContext();
diff --git a/Tag.cs b/Tag.cs
index 810c49c..70d70c9 100644
--- a/Tag.cs
+++ b/Tag.cs
@@ -10,15 +10,15 @@ public enum TagSource {
}
public class TagDefinition : HBObject {
- public TagSource Source { get; set; } = TagSource.Internal;
- public string? Namespace { get; set; }
- public string Name { get; set; }
- public virtual List<TagDefinition> ImplicitTags { get; set; } = new();
+ public TagSource Source { get; set; } = TagSource.Internal;
+ public string? Namespace { get; set; }
+ public string Name { get; set; }
+ public virtual List<TagDefinition> ImplicitTags { get; set; } = new();
}
public class Tag : HBObject {
public virtual TagDefinition TagDefinition { get; set; }
- public DateTime CreateTime { get; set; } = DateTime.Now;
+ public DateTime CreateTime { get; set; } = DateTime.UtcNow;
public virtual HBObject Target { get; set; }
public Tag() {}
diff --git a/appsettings.json b/appsettings.json
index 10f68b8..90977fc 100644
--- a/appsettings.json
+++ b/appsettings.json
@@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Warning"
}
},
+ "ConnectionStrings": {
+ "DefaultConnection": "Host=127.0.0.1;Database=HyperBooru;Username=hyperbooru;Password=password"
+ },
"AllowedHosts": "*"
}