summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HBContext.cs14
-rw-r--r--MainLayout.razor8
-rw-r--r--MainLayout.razor.css14
-rw-r--r--Migrations/20230818012410_SeedGuids.Designer.cs258
-rw-r--r--Migrations/20230818012410_SeedGuids.cs47
-rw-r--r--Migrations/HBContextModelSnapshot.cs4
-rw-r--r--Pages/Gallery.razor52
-rw-r--r--Pages/Gallery.razor.css (renamed from Pages/Index.razor.css)0
-rw-r--r--Pages/Index.razor17
-rw-r--r--Pages/Upload.razor21
-rw-r--r--Pages/Upload.razor.css28
-rw-r--r--Pages/_Host.cshtml1
-rw-r--r--Program.cs1
-rw-r--r--Server.csproj1
-rw-r--r--Services/SearchService.cs36
-rw-r--r--_Imports.razor3
16 files changed, 477 insertions, 28 deletions
diff --git a/HBContext.cs b/HBContext.cs
index 04f2d1a..45b8852 100644
--- a/HBContext.cs
+++ b/HBContext.cs
@@ -35,8 +35,18 @@ public class HBContext : DbContext {
// 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" }
+ new() {
+ ObjectId = -1,
+ Guid = new("EBDAD4F8-455A-4351-8017-1D4854D6FA38"),
+ Source = TagSource.Internal,
+ Name = "nsfw"
+ },
+ new() {
+ ObjectId = -2,
+ Guid = new("EA212801-5BCC-4C0E-814F-FB9D30DB58BC"),
+ Source = TagSource.Internal,
+ Name = "ingest"
+ }
});
// Implicit tags need some special attention to make many<->many
diff --git a/MainLayout.razor b/MainLayout.razor
index bd0675b..111e307 100644
--- a/MainLayout.razor
+++ b/MainLayout.razor
@@ -5,9 +5,13 @@
<div id="navbar">
<a href="/">Home</a>
<a href="/TagDefinitions">Tags</a>
- <input type="text" placeholder="Search"/>
+ <a href="/Gallery?ingest=true">Ingest</a>
+ <a href="/Upload">Upload</a>
+ <form action="/Gallery" method="get">
+ <input type="text" name="q" placeholder="Search"/>
+ </form>
</div>
-@* <div id="content" style="overflow-y:@(ViewBag.ContentScroll ? "auto" : "hidden");padding:@(ViewBag.ContentMargin ?? "0");"> *@
+
<div id="content">
@Body
</div>
diff --git a/MainLayout.razor.css b/MainLayout.razor.css
index e82e72e..3d03d61 100644
--- a/MainLayout.razor.css
+++ b/MainLayout.razor.css
@@ -20,15 +20,21 @@ div#navbar > a:active {
color: var(--col-navbar-bg);
}
-div#navbar > input {
+div#navbar form {
+ display: flex;
+ margin: 0 20px 0 auto;
+ min-width: 30%;
+}
+
+div#navbar input[type="text"] {
align-self: center;
background: var(--col-bg);
border-radius: 0;
color: white;
- height: 40px !important;
- margin: 0 20px 0 auto;
font-size: 12pt;
- min-width: 30%;
+ height: 40px !important;
+ margin: 0;
+ width: 100%;
}
#content {
diff --git a/Migrations/20230818012410_SeedGuids.Designer.cs b/Migrations/20230818012410_SeedGuids.Designer.cs
new file mode 100644
index 0000000..cb09634
--- /dev/null
+++ b/Migrations/20230818012410_SeedGuids.Designer.cs
@@ -0,0 +1,258 @@
+// <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("20230818012410_SeedGuids")]
+ partial class SeedGuids
+ {
+ /// <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)
+ .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.UploadedFile", b =>
+ {
+ b.Property<int>("UploadedFileId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UploadedFileId"));
+
+ 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<int>("MediaObjectId")
+ .HasColumnType("integer");
+
+ b.Property<string>("OriginalChecksum")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property<DateTime>("UploadTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("UploadedFileId");
+
+ b.HasIndex("MediaObjectId");
+
+ b.ToTable("UploadedFiles", (string)null);
+ });
+
+ 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<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("timestamp with time zone");
+
+ 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.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.HasOne("HyperBooru.Media", "Media")
+ .WithMany("UploadedFiles")
+ .HasForeignKey("MediaObjectId")
+ .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("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();
+ });
+
+ modelBuilder.Entity("HyperBooru.HBObject", b =>
+ {
+ b.Navigation("Tags");
+ });
+
+ modelBuilder.Entity("HyperBooru.Media", b =>
+ {
+ b.Navigation("UploadedFiles");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20230818012410_SeedGuids.cs b/Migrations/20230818012410_SeedGuids.cs
new file mode 100644
index 0000000..47749f7
--- /dev/null
+++ b/Migrations/20230818012410_SeedGuids.cs
@@ -0,0 +1,47 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace HyperBooru.Migrations
+{
+ /// <inheritdoc />
+ public partial class SeedGuids : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.UpdateData(
+ table: "Objects",
+ keyColumn: "ObjectId",
+ keyValue: -2,
+ column: "Guid",
+ value: new Guid("ea212801-5bcc-4c0e-814f-fb9d30db58bc"));
+
+ migrationBuilder.UpdateData(
+ table: "Objects",
+ keyColumn: "ObjectId",
+ keyValue: -1,
+ column: "Guid",
+ value: new Guid("ebdad4f8-455a-4351-8017-1d4854d6fa38"));
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.UpdateData(
+ table: "Objects",
+ keyColumn: "ObjectId",
+ keyValue: -2,
+ column: "Guid",
+ value: new Guid("bab0a0f2-b126-429c-a80e-4f339a1f4bb2"));
+
+ migrationBuilder.UpdateData(
+ table: "Objects",
+ keyColumn: "ObjectId",
+ keyValue: -1,
+ column: "Guid",
+ value: new Guid("e906ff13-ee7d-4eb2-826b-1cf880590f6b"));
+ }
+ }
+}
diff --git a/Migrations/HBContextModelSnapshot.cs b/Migrations/HBContextModelSnapshot.cs
index aac8471..e535376 100644
--- a/Migrations/HBContextModelSnapshot.cs
+++ b/Migrations/HBContextModelSnapshot.cs
@@ -158,14 +158,14 @@ namespace HyperBooru.Migrations
new
{
ObjectId = -1,
- Guid = new Guid("e906ff13-ee7d-4eb2-826b-1cf880590f6b"),
+ Guid = new Guid("ebdad4f8-455a-4351-8017-1d4854d6fa38"),
Name = "nsfw",
Source = 0
},
new
{
ObjectId = -2,
- Guid = new Guid("bab0a0f2-b126-429c-a80e-4f339a1f4bb2"),
+ Guid = new Guid("ea212801-5bcc-4c0e-814f-fb9d30db58bc"),
Name = "ingest",
Source = 0
});
diff --git a/Pages/Gallery.razor b/Pages/Gallery.razor
new file mode 100644
index 0000000..fb58ca0
--- /dev/null
+++ b/Pages/Gallery.razor
@@ -0,0 +1,52 @@
+@page "/"
+@page "/Gallery"
+@inject IDbContextFactory<HBContext> dbFactory
+@inject ISearchService searchService
+
+<PageTitle>@(Query is null ? "Gallery" : "Search Results")</PageTitle>
+
+<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/>
+
+<form id="upload" action="/media" method="post" enctype="multipart/form-data">
+ <input type="file" id="myFile" name="filename"/>
+ <input type="submit" />
+</form>
+
+@foreach(var media in Media) {
+ <a href="/ViewMedia?m=@(media.Guid)">
+ <img src="/media/thumb/@(media.Guid)?h=200" />
+ </a>
+}
+
+@code {
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "q")]
+ public string? Query { get; set; }
+
+ [Parameter]
+ [SupplyParameterFromQuery]
+ public bool? Ingest { get; set; }
+
+ private Media[] Media;
+
+ protected override void OnParametersSet() => LoadMedia();
+
+ private void LoadMedia() {
+ using var db = dbFactory.CreateDbContext();
+
+ if(Ingest is not null && Ingest == true) {
+ Media = db.Media
+ .OrderByDescending(m => m.ObjectId)
+ .ToArray()
+ .Where(m => m.IsIngest)
+ .ToArray();
+ } else {
+ if(Query is null)
+ Media = db.Media
+ .OrderByDescending(m => m.ObjectId)
+ .ToArray();
+ else
+ Media = searchService.Search(Query);
+ }
+ }
+}
diff --git a/Pages/Index.razor.css b/Pages/Gallery.razor.css
index d1750b4..d1750b4 100644
--- a/Pages/Index.razor.css
+++ b/Pages/Gallery.razor.css
diff --git a/Pages/Index.razor b/Pages/Index.razor
deleted file mode 100644
index 4719530..0000000
--- a/Pages/Index.razor
+++ /dev/null
@@ -1,17 +0,0 @@
-@page "/"
-@inject HBContext db;
-
-<PageTitle>Gallery</PageTitle>
-
-<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/>
-
-<form id="upload" action="/media" method="post" enctype="multipart/form-data">
- <input type="file" id="myFile" name="filename"/>
- <input type="submit" />
-</form>
-
-@foreach(var media in db.Media.OrderByDescending(m => m.ObjectId)) {
- <a href="/ViewMedia?m=@(media.Guid)">
- <img src="/media/thumb/@(media.Guid)?h=200" />
- </a>
-}
diff --git a/Pages/Upload.razor b/Pages/Upload.razor
new file mode 100644
index 0000000..33153d2
--- /dev/null
+++ b/Pages/Upload.razor
@@ -0,0 +1,21 @@
+@page "/Upload"
+
+<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/>
+
+<div id="dropzone" class="@((dropHover ? "drop-hover" : ""))">
+ <InputFile
+ multiple
+ title=""
+ accept="image/*"
+ OnChange=FileInputChange
+ @ondragenter=@(() => dropHover = true)
+ @ondragleave=@(() => dropHover = false)/>
+ <p>Drag a file to upload it</p>
+</div>
+
+@code {
+ private bool dropHover = false;
+
+ private void FileInputChange(IFileListEntry[] files) {
+ }
+} \ No newline at end of file
diff --git a/Pages/Upload.razor.css b/Pages/Upload.razor.css
new file mode 100644
index 0000000..4064467
--- /dev/null
+++ b/Pages/Upload.razor.css
@@ -0,0 +1,28 @@
+div#dropzone {
+ border-radius: 10px;
+ border: 3px dashed #aaa;
+ height: 400px;
+ left: 50%;
+ position: relative;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 700px;
+}
+
+div#dropzone p {
+ color: #aaa;
+ display: inline-block;
+ left: 50%;
+ margin: 0;
+ position: relative;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+
+div#dropzone.drop-hover {
+ border: 3px dashed white;
+}
+
+div#dropzone.drop-hover p {
+ color: white;
+} \ No newline at end of file
diff --git a/Pages/_Host.cshtml b/Pages/_Host.cshtml
index e01b94d..e519d57 100644
--- a/Pages/_Host.cshtml
+++ b/Pages/_Host.cshtml
@@ -12,6 +12,7 @@
<link href="/styles/global.css" rel="stylesheet" />
<link href="/favicon.ico" rel="icon" />
<link href="/manifest.webmanifest" rel="manifest" />
+ <script src="~/_content/BlazorInputFile/inputfile.js"></script>
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
diff --git a/Program.cs b/Program.cs
index dfc635c..4796825 100644
--- a/Program.cs
+++ b/Program.cs
@@ -17,6 +17,7 @@ public class Program {
// Add out custom services
builder.Services.AddSingleton<IConfigService, ConfigService>();
builder.Services.AddDbContextFactory<HBContext>();
+ builder.Services.AddScoped<ISearchService, SearchService>();
builder.Services.AddScoped<ITagService, TagService>();
var app = builder.Build();
diff --git a/Server.csproj b/Server.csproj
index f762884..ea8b63d 100644
--- a/Server.csproj
+++ b/Server.csproj
@@ -20,6 +20,7 @@
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="BlazorInputFile" Version="0.2.0" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="13.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
diff --git a/Services/SearchService.cs b/Services/SearchService.cs
new file mode 100644
index 0000000..681c08d
--- /dev/null
+++ b/Services/SearchService.cs
@@ -0,0 +1,36 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace HyperBooru.Services;
+
+public interface ISearchService {
+ public Media[] Search(string query);
+}
+
+public class SearchService : ISearchService {
+ private IDbContextFactory<HBContext> dbFactory;
+
+ public SearchService(IDbContextFactory<HBContext> dbFactory) =>
+ this.dbFactory = dbFactory;
+
+ public Media[] Search(string query) {
+ var db = dbFactory.CreateDbContext();
+
+ query = query.ToLower();
+
+ var matchedTag = db.TagDefinitions
+ .FirstOrDefault(td => td.Name.ToLower() == query)?.Guid;
+ if(matchedTag is not null)
+ return db.Media
+ .Where(m => m.Tags
+ .Select(t => t.TagDefinition)
+ .Select(td => td.Guid)
+ .Contains((Guid) matchedTag))
+ .ToArray();
+
+ var matchingTags = db.TagDefinitions
+ .Where(td => td.Name.ToLower().Contains(query))
+ .Select(td => td.Guid);
+
+ throw new NotImplementedException();
+ }
+}
diff --git a/_Imports.razor b/_Imports.razor
index d20a491..61dd370 100644
--- a/_Imports.razor
+++ b/_Imports.razor
@@ -1,4 +1,5 @@
-@using HyperBooru
+@using BlazorInputFile
+@using HyperBooru
@using HyperBooru.Pages.Component
@using HyperBooru.Services
@using Microsoft.AspNetCore.Components.Routing