diff options
| -rw-r--r-- | HBContext.cs | 14 | ||||
| -rw-r--r-- | MainLayout.razor | 8 | ||||
| -rw-r--r-- | MainLayout.razor.css | 14 | ||||
| -rw-r--r-- | Migrations/20230818012410_SeedGuids.Designer.cs | 258 | ||||
| -rw-r--r-- | Migrations/20230818012410_SeedGuids.cs | 47 | ||||
| -rw-r--r-- | Migrations/HBContextModelSnapshot.cs | 4 | ||||
| -rw-r--r-- | Pages/Gallery.razor | 52 | ||||
| -rw-r--r-- | Pages/Gallery.razor.css (renamed from Pages/Index.razor.css) | 0 | ||||
| -rw-r--r-- | Pages/Index.razor | 17 | ||||
| -rw-r--r-- | Pages/Upload.razor | 21 | ||||
| -rw-r--r-- | Pages/Upload.razor.css | 28 | ||||
| -rw-r--r-- | Pages/_Host.cshtml | 1 | ||||
| -rw-r--r-- | Program.cs | 1 | ||||
| -rw-r--r-- | Server.csproj | 1 | ||||
| -rw-r--r-- | Services/SearchService.cs | 36 | ||||
| -rw-r--r-- | _Imports.razor | 3 |
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> @@ -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 |
