diff options
| -rw-r--r-- | .editorconfig | 7 | ||||
| -rw-r--r-- | .gitattributes | 63 | ||||
| -rw-r--r-- | App.razor | 19 | ||||
| -rw-r--r-- | Controllers/LoginController.cs | 49 | ||||
| -rw-r--r-- | MainLayout.razor | 11 | ||||
| -rw-r--r-- | MainLayout.razor.css | 9 | ||||
| -rw-r--r-- | Pages/Component/NsfwSwitch.razor | 8 | ||||
| -rw-r--r-- | Pages/Component/Titlebar.razor | 87 | ||||
| -rw-r--r-- | Pages/Login.razor | 17 | ||||
| -rw-r--r-- | Pages/_Host.cshtml | 37 | ||||
| -rw-r--r-- | Properties/launchSettings.json | 31 | ||||
| -rw-r--r-- | Server.Client/Client.csproj | 22 | ||||
| -rw-r--r-- | Server.Client/IDialog.cs (renamed from IDialog.cs) | 2 | ||||
| -rw-r--r-- | Server.Client/LICENSE.txt (renamed from LICENSE.txt) | 0 | ||||
| -rw-r--r-- | Server.Client/Layout/MainLayout.razor | 15 | ||||
| -rw-r--r-- | Server.Client/Layout/MainLayout.razor.css | 30 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/AboutDialog.razor (renamed from Pages/Component/AboutDialog.razor) | 24 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/AboutDialog.razor.css (renamed from Pages/Component/AboutDialog.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/ButtonContainer.razor (renamed from Pages/Component/ButtonContainer.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/ButtonContainer.razor.css (renamed from Pages/Component/ButtonContainer.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Dialog.razor (renamed from Pages/Component/Dialog.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Dialog.razor.css (renamed from Pages/Component/Dialog.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/MediaTagTable.razor (renamed from Pages/Component/MediaTagTable.razor) | 39 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/MediaTagTable.razor.css (renamed from Pages/Component/MediaTagTable.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/MobileMenu.razor (renamed from Pages/Component/MobileMenu.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/MobileMenu.razor.css (renamed from Pages/Component/MobileMenu.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/NsfwSwitch.razor | 6 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/ProgressBar.razor (renamed from Pages/Component/ProgressBar.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/ProgressBar.razor.css (renamed from Pages/Component/ProgressBar.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/RedirectLogin.razor (renamed from Pages/Component/RedirectLogin.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Switch.razor (renamed from Pages/Component/Switch.razor) | 4 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Switch.razor.css (renamed from Pages/Component/Switch.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TabContainer.razor (renamed from Pages/Component/TabContainer.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TabContainer.razor.css (renamed from Pages/Component/TabContainer.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TabPane.razor (renamed from Pages/Component/TabPane.razor) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TabPane.razor.css (renamed from Pages/Component/TabPane.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TagEditDialog.razor (renamed from Pages/Component/TagEditDialog.razor) | 8 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TagEditDialog.razor.css (renamed from Pages/Component/TagEditDialog.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TagSelectDialog.razor (renamed from Pages/Component/TagSelectDialog.razor) | 101 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/TagSelectDialog.razor.css (renamed from Pages/Component/TagSelectDialog.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Titlebar.razor | 98 | ||||
| -rw-r--r-- | Server.Client/Pages/Component/Titlebar.razor.css (renamed from Pages/Component/Titlebar.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Gallery.razor (renamed from Pages/Gallery.razor) | 94 | ||||
| -rw-r--r-- | Server.Client/Pages/Gallery.razor.css (renamed from Pages/Gallery.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Login.razor | 17 | ||||
| -rw-r--r-- | Server.Client/Pages/Login.razor.css (renamed from Pages/Login.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/NotFound.razor | 5 | ||||
| -rw-r--r-- | Server.Client/Pages/TagDefinitions.razor (renamed from Pages/TagDefinitions.razor) | 126 | ||||
| -rw-r--r-- | Server.Client/Pages/TagDefinitions.razor.css (renamed from Pages/TagDefinitions.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/Upload.razor (renamed from Pages/Upload.razor) | 1 | ||||
| -rw-r--r-- | Server.Client/Pages/Upload.razor.css (renamed from Pages/Upload.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Pages/ViewMedia.razor (renamed from Pages/ViewMedia.razor) | 139 | ||||
| -rw-r--r-- | Server.Client/Pages/ViewMedia.razor.css (renamed from Pages/ViewMedia.razor.css) | 0 | ||||
| -rw-r--r-- | Server.Client/Program.cs | 16 | ||||
| -rw-r--r-- | Server.Client/Routes.razor | 6 | ||||
| -rw-r--r-- | Server.Client/_Imports.razor | 13 | ||||
| -rw-r--r-- | Server.Client/wwwroot/appsettings.Development.json | 8 | ||||
| -rw-r--r-- | Server.Client/wwwroot/appsettings.json | 8 | ||||
| -rw-r--r-- | Server/Components/App.razor | 24 | ||||
| -rw-r--r-- | Server/Components/Pages/Error.razor | 36 | ||||
| -rw-r--r-- | Server/Components/_Imports.razor | 11 | ||||
| -rw-r--r-- | Server/Controllers/ApiFeedController.cs (renamed from Controllers/ApiFeedController.cs) | 2 | ||||
| -rw-r--r-- | Server/Controllers/ApiMediaController.cs (renamed from Controllers/ApiMediaController.cs) | 2 | ||||
| -rw-r--r-- | Server/Controllers/ApiTagController.cs (renamed from Controllers/ApiTagController.cs) | 1 | ||||
| -rw-r--r-- | Server/Controllers/ApiUserController.cs (renamed from Controllers/ApiUserController.cs) | 0 | ||||
| -rw-r--r-- | Server/Controllers/MediaController.cs (renamed from Controllers/MediaController.cs) | 1 | ||||
| -rw-r--r-- | Server/Dockerfile (renamed from Dockerfile) | 0 | ||||
| -rw-r--r-- | Server/ExceptionMiddleware.cs (renamed from ExceptionMiddleware.cs) | 0 | ||||
| -rw-r--r-- | Server/HBContext.cs (renamed from HBContext.cs) | 0 | ||||
| -rw-r--r-- | Server/HBObject.cs (renamed from HBObject.cs) | 0 | ||||
| -rw-r--r-- | Server/LICENSE.txt | 661 | ||||
| -rw-r--r-- | Server/Media.cs (renamed from Media.cs) | 0 | ||||
| -rw-r--r-- | Server/Migrations/20260131125650_InitialMigration.Designer.cs (renamed from Migrations/20260131125650_InitialMigration.Designer.cs) | 0 | ||||
| -rw-r--r-- | Server/Migrations/20260131125650_InitialMigration.cs (renamed from Migrations/20260131125650_InitialMigration.cs) | 0 | ||||
| -rw-r--r-- | Server/Migrations/HBContextModelSnapshot.cs (renamed from Migrations/HBContextModelSnapshot.cs) | 0 | ||||
| -rw-r--r-- | Server/Program.cs (renamed from Program.cs) | 52 | ||||
| -rw-r--r-- | Server/Properties/launchSettings.json | 25 | ||||
| -rw-r--r-- | Server/Server.csproj (renamed from Server.csproj) | 35 | ||||
| -rw-r--r-- | Server/Services/ConfigService.cs (renamed from Services/ConfigService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/FeedService.cs (renamed from Services/FeedService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/MediaService.cs (renamed from Services/MediaService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/OcrService.cs (renamed from Services/OcrService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/SourceService.cs (renamed from Services/SourceService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/TagService.cs (renamed from Services/TagService.cs) | 0 | ||||
| -rw-r--r-- | Server/Services/UserService.cs (renamed from Services/UserService.cs) | 0 | ||||
| -rw-r--r-- | Server/Tag.cs (renamed from Tag.cs) | 0 | ||||
| -rw-r--r-- | Server/Todo.md (renamed from Todo.md) | 0 | ||||
| -rw-r--r-- | Server/User.cs (renamed from User.cs) | 0 | ||||
| -rw-r--r-- | Server/Util.cs (renamed from Util.cs) | 0 | ||||
| -rw-r--r-- | Server/appsettings.Development.json | 8 | ||||
| -rw-r--r-- | Server/appsettings.json (renamed from appsettings.json) | 0 | ||||
| -rw-r--r-- | Server/dotnet-tools.json (renamed from .config/dotnet-tools.json) | 2 | ||||
| -rw-r--r-- | Server/tessdata/eng.traineddata (renamed from tessdata/eng.traineddata) | bin | 15400601 -> 15400601 bytes | |||
| -rw-r--r-- | Server/wwwroot/app.css | 60 | ||||
| -rw-r--r-- | Server/wwwroot/css/site.css (renamed from wwwroot/css/site.css) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/favicon.ico (renamed from wwwroot/favicon.ico) | bin | 3262 -> 3262 bytes | |||
| -rw-r--r-- | Server/wwwroot/icon-192.png (renamed from wwwroot/icon-192.png) | bin | 31523 -> 31523 bytes | |||
| -rw-r--r-- | Server/wwwroot/icon-512.png (renamed from wwwroot/icon-512.png) | bin | 136487 -> 136487 bytes | |||
| -rw-r--r-- | Server/wwwroot/images/book.svg (renamed from wwwroot/images/book.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/checkmark.svg (renamed from wwwroot/images/checkmark.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/cross.svg (renamed from wwwroot/images/cross.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/edit.svg (renamed from wwwroot/images/edit.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/info.svg (renamed from wwwroot/images/info.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/loginbg.webp (renamed from wwwroot/images/loginbg.webp) | bin | 2247672 -> 2247672 bytes | |||
| -rw-r--r-- | Server/wwwroot/images/photo.svg (renamed from wwwroot/images/photo.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/tag.svg (renamed from wwwroot/images/tag.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/images/trash.svg (renamed from wwwroot/images/trash.svg) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/js/dialog.js (renamed from wwwroot/js/dialog.js) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/js/keyboard.js (renamed from wwwroot/js/keyboard.js) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/js/mobile.js (renamed from wwwroot/js/mobile.js) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/loginbg.webm (renamed from wwwroot/loginbg.webm) | bin | 390877 -> 390877 bytes | |||
| -rw-r--r-- | Server/wwwroot/manifest.webmanifest (renamed from wwwroot/manifest.webmanifest) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/styles/data-table.css (renamed from wwwroot/styles/data-table.css) | 0 | ||||
| -rw-r--r-- | Server/wwwroot/styles/global.css (renamed from wwwroot/styles/global.css) | 0 | ||||
| -rw-r--r-- | _Imports.razor | 9 | ||||
| -rw-r--r-- | appsettings.Development.json | 19 |
116 files changed, 1382 insertions, 686 deletions
diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 913b3e5..0000000 --- a/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -[*.cs] - -# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -dotnet_diagnostic.CS8618.severity = silent - -# ASP0000: Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' -dotnet_diagnostic.ASP0000.severity = silent diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c42..0000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/App.razor b/App.razor deleted file mode 100644 index b4e47c9..0000000 --- a/App.razor +++ /dev/null @@ -1,19 +0,0 @@ -<CascadingAuthenticationState> - <Router AppAssembly="@typeof(App).Assembly"> - <Found Context="routeData"> - <AuthorizeRouteView - RouteData="@routeData" - DefaultLayout="@typeof(MainLayout)"> - <NotAuthorized> - <RedirectLogin/> - </NotAuthorized> - </AuthorizeRouteView> - </Found> - <NotFound> - <PageTitle>Not found</PageTitle> - <LayoutView Layout="@typeof(MainLayout)"> - <p role="alert">Sorry, there's nothing at this address.</p> - </LayoutView> - </NotFound> - </Router> -</CascadingAuthenticationState> diff --git a/Controllers/LoginController.cs b/Controllers/LoginController.cs deleted file mode 100644 index c93f0d5..0000000 --- a/Controllers/LoginController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using HyperBooru.Services; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Mvc; -using System.Security.Claims; - -namespace HyperBooru.Controllers; - -[ApiController] -[Route("/")] -public class LoginController : Controller { - private IHttpContextAccessor httpContextAccessor; - - public LoginController(IHttpContextAccessor httpContextAccessor) => - this.httpContextAccessor = httpContextAccessor; - - [HttpPost("Login")] - public async Task<IActionResult> Login( - [FromForm] string username, - [FromForm] string password, - HBContext db) { - - var user = db.Users.FirstOrDefault(u => u.Username == username); - if(user is null) - return StatusCode(403); - - var hash = UserService.HashPassword(password); - if(hash != user.PasswordHash) - return StatusCode(403); - - var claims = new Claim[] { - new Claim(ClaimTypes.Name, user.Username), - new Claim("ObjectId", user.ObjectId.ToString()) - }; - - var claimsIdentity = new ClaimsIdentity( - claims, - CookieAuthenticationDefaults.AuthenticationScheme); - - var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); - - await httpContextAccessor.HttpContext!.SignInAsync(claimsPrincipal); - return Ok(); - } - - [HttpPost("Logout")] - public async Task Logout() => - await httpContextAccessor.HttpContext!.SignOutAsync(); -} diff --git a/MainLayout.razor b/MainLayout.razor deleted file mode 100644 index 8e9f6bd..0000000 --- a/MainLayout.razor +++ /dev/null @@ -1,11 +0,0 @@ -@inherits LayoutComponentBase - -<link href="@(nameof(HyperBooru)).styles.css" rel="stylesheet" /> - -<Titlebar/> - -<MobileMenu/> - -<div id="content"> - @Body -</div> diff --git a/MainLayout.razor.css b/MainLayout.razor.css deleted file mode 100644 index c2b5603..0000000 --- a/MainLayout.razor.css +++ /dev/null @@ -1,9 +0,0 @@ -div#content { - flex: 1 1 calc(100vh - 59px); - overflow-x: hidden; - overflow-y: auto; -} - -body.mobile-menu-visible div#content { - display: none; -} diff --git a/Pages/Component/NsfwSwitch.razor b/Pages/Component/NsfwSwitch.razor deleted file mode 100644 index b96606d..0000000 --- a/Pages/Component/NsfwSwitch.razor +++ /dev/null @@ -1,8 +0,0 @@ -@inject IUserService userService - -<Switch InitialValue=userService.UserSessionState.ShowNsfw OnToggle=ToggleNsfw/> - -@code { - private void ToggleNsfw(bool showNsfw) => - userService.UserSessionState.ShowNsfw = showNsfw; -}
\ No newline at end of file diff --git a/Pages/Component/Titlebar.razor b/Pages/Component/Titlebar.razor deleted file mode 100644 index 48257b2..0000000 --- a/Pages/Component/Titlebar.razor +++ /dev/null @@ -1,87 +0,0 @@ -@inject IJSRuntime jsRuntime -@inject IUserService userService - -<script suppress-error="BL9992"> - async function login() { - var username = document.querySelector('input#username'); - var password = document.querySelector('input#password'); - - var formData = new FormData(); - formData.append('username', username.value); - formData.append('password', password.value); - - var resp = await fetch('/Login', { - method: 'POST', - body: formData - }); - - if(resp.ok) { - window.location.href = '/'; - } else if(resp.status == 403) { - var form = document.querySelector('form.login'); - form.classList.remove('bad-login'); - @* TODO: improve this hacky method of triggering reflow *@ - form.offsetWidth; - form.classList.add('bad-login'); - username.value = password.value = null; - username.focus(); - } else { - alert('Unknown error while attempting to login!'); - } - } - - async function logout() { - var resp = await fetch('/Logout', { method: 'POST' }); - if(resp.ok) { - window.location.href = '/Login'; - } else { - alert('Error logging out!'); - } - } -</script> - -<AuthorizeView> - <Authorized> - <div id="navbar"> - <p class="mobile">HyperBooru</p> - <a class="mobile menu-button" href="javascript:toggleMobileMenu();">☰</a> - - <a class="desktop" href="/">Home</a> - <a class="desktop" href="/TagDefinitions">Tags</a> - <a class="desktop" href="/Gallery?ingest=true">Ingest</a> - <a class="desktop" href="/Upload">Upload</a> - <a class="desktop" href="javascript:;" @onclick=@(() => aboutDialog.Show())>About</a> - - <p class="desktop" id="nsfw-label">NSFW</p> - <div id="nsfw-switch" class="desktop"> - <NsfwSwitch/> - </div> - <form action="/Gallery" method="get" class="desktop"> - <input type="text" name="q" placeholder="Search"/> - </form> - <a class="desktop" href="javascript:logout();">Logout</a> - </div> - <AboutDialog @ref=aboutDialog/> - </Authorized> - <NotAuthorized> - <div id="navbar"> - <h2>Login</h2> - <form class="login" action="javascript:login();"> - <input - id="username" - placeholder="Username" - type="text" - autocorrect="off" - autocapitalize="off" - autocomplete="off" - autofocus/> - <input id="password" placeholder="Password" type="password"/> - </form> - <a href="javascript:login();">Login</a> - </div> - </NotAuthorized> -</AuthorizeView> - -@code { - private AboutDialog aboutDialog; -} diff --git a/Pages/Login.razor b/Pages/Login.razor deleted file mode 100644 index 723a78a..0000000 --- a/Pages/Login.razor +++ /dev/null @@ -1,17 +0,0 @@ -@page "/Login" -@inject NavigationManager navigationManager - -<PageTitle>HyperBooru Login</PageTitle> - -<div/> - -@code { - [CascadingParameter] - public Task<AuthenticationState> AuthenticationState{ get; set; } - - protected override void OnInitialized() { - var authState = AuthenticationState.GetAwaiter().GetResult(); - if(authState!.User.Identity?.IsAuthenticated ?? false) - navigationManager.NavigateTo("/"); - } -}
\ No newline at end of file diff --git a/Pages/_Host.cshtml b/Pages/_Host.cshtml deleted file mode 100644 index 28ff24c..0000000 --- a/Pages/_Host.cshtml +++ /dev/null @@ -1,37 +0,0 @@ -@page "/" -@using Microsoft.AspNetCore.Components.Web -@namespace HyperBooru.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> - <base href="~/" /> - <link href="css/site.css" rel="stylesheet" /> - <link href="/styles/global.css" rel="stylesheet" /> - <link href="/favicon.ico" rel="icon" /> - <link href="/manifest.webmanifest" rel="manifest" /> - <script type="text/javascript" src="/js/dialog.js"></script> - <script type="text/javascript" src="/js/keyboard.js"></script> - <script type="text/javascript" src="/js/mobile.js"></script> - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> -</head> -<body> - <component type="typeof(App)" render-mode="ServerPrerendered" /> - - <div id="blazor-error-ui"> - <environment include="Staging,Production"> - An error has occurred. This application may no longer respond until reloaded. - </environment> - <environment include="Development"> - An unhandled exception has occurred. See browser dev tools for details. - </environment> - <a href="" class="reload">Reload</a> - <a class="dismiss">🗙</a> - </div> - - <script src="_framework/blazor.server.js"></script> -</body> -</html> diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json deleted file mode 100644 index 9f4966c..0000000 --- a/Properties/launchSettings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "launchBrowser": true, - "launchUrl": "https://localhost:7132", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "https://localhost:7132;http://localhost:5186" - }, - "distributionName": "" - }, - "HyperBooru": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7132;http://localhost:5186" - } - }, - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:1922", - "sslPort": 44354 - } - } -}
\ No newline at end of file diff --git a/Server.Client/Client.csproj b/Server.Client/Client.csproj new file mode 100644 index 0000000..19c4f39 --- /dev/null +++ b/Server.Client/Client.csproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> + + <PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile> + <StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode> + <BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException> + <AssemblyName>HyperBooru.Client</AssemblyName> + <RootNamespace>HyperBooru.Client</RootNamespace> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.8" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\ApiClient\ApiClient.csproj" /> + </ItemGroup> + +</Project> diff --git a/IDialog.cs b/Server.Client/IDialog.cs index 41e86a8..6fc4646 100644 --- a/IDialog.cs +++ b/Server.Client/IDialog.cs @@ -1,4 +1,4 @@ -namespace HyperBooru; +namespace HyperBooru.Client; public interface IDialog { public bool Visible { get; set; } diff --git a/LICENSE.txt b/Server.Client/LICENSE.txt index 0ad25db..0ad25db 100644 --- a/LICENSE.txt +++ b/Server.Client/LICENSE.txt diff --git a/Server.Client/Layout/MainLayout.razor b/Server.Client/Layout/MainLayout.razor new file mode 100644 index 0000000..dc8b923 --- /dev/null +++ b/Server.Client/Layout/MainLayout.razor @@ -0,0 +1,15 @@ +@inherits LayoutComponentBase + +<Titlebar/> + +<MobileMenu/> + +<div id="content"> + @Body +</div> + +<div id="blazor-error-ui" data-nosnippet> + An unhandled error has occurred. + <a href="." class="reload">Reload</a> + <span class="dismiss">🗙</span> +</div> diff --git a/Server.Client/Layout/MainLayout.razor.css b/Server.Client/Layout/MainLayout.razor.css new file mode 100644 index 0000000..7e84358 --- /dev/null +++ b/Server.Client/Layout/MainLayout.razor.css @@ -0,0 +1,30 @@ +div#content { + flex: 1 1 calc(100vh - 59px); + overflow-x: hidden; + overflow-y: auto; +} + +body.mobile-menu-visible div#content { + display: none; +} + +#blazor-error-ui { + color-scheme: light only; + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + box-sizing: border-box; + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/Pages/Component/AboutDialog.razor b/Server.Client/Pages/Component/AboutDialog.razor index 1229dc7..0ef8473 100644 --- a/Pages/Component/AboutDialog.razor +++ b/Server.Client/Pages/Component/AboutDialog.razor @@ -1,8 +1,8 @@ @using System.Reflection -@using Microsoft.AspNetCore.Hosting +@* @using Microsoft.AspNetCore.Hosting @inject IDbContextFactory<HBContext> dbFactory -@inject IHostingEnvironment hostingEnvironment -@implements IDialog +@inject IHostingEnvironment hostingEnvironment*@ + @implements IDialog <Dialog @ref=dialog> <p id="title">@Title</p> @@ -699,15 +699,15 @@ set { dialog.Visible = value; if(value) { - using var db = dbFactory.CreateDbContext(); - progress = ( - Untagged: db.Media - .Where(m => m.Tags.Any(t => t.TagDefinition.ObjectId == (int) HBObjectId.IngestTag)) - .Count(), - Total: db.Media.Count() - ); - progressBar.Progress = (float) progress.Value!.Untagged / (float) progress.Value!.Total; - InvokeAsync(() => StateHasChanged()); + // using var db = dbFactory.CreateDbContext(); + // progress = ( + // Untagged: db.Media + // .Where(m => m.Tags.Any(t => t.TagDefinition.ObjectId == (int) HBObjectId.IngestTag)) + // .Count(), + // Total: db.Media.Count() + // ); + // progressBar.Progress = (float) progress.Value!.Untagged / (float) progress.Value!.Total; + // InvokeAsync(() => StateHasChanged()); } } } diff --git a/Pages/Component/AboutDialog.razor.css b/Server.Client/Pages/Component/AboutDialog.razor.css index cbc4b59..cbc4b59 100644 --- a/Pages/Component/AboutDialog.razor.css +++ b/Server.Client/Pages/Component/AboutDialog.razor.css diff --git a/Pages/Component/ButtonContainer.razor b/Server.Client/Pages/Component/ButtonContainer.razor index a631dcc..a631dcc 100644 --- a/Pages/Component/ButtonContainer.razor +++ b/Server.Client/Pages/Component/ButtonContainer.razor diff --git a/Pages/Component/ButtonContainer.razor.css b/Server.Client/Pages/Component/ButtonContainer.razor.css index c2d5d25..c2d5d25 100644 --- a/Pages/Component/ButtonContainer.razor.css +++ b/Server.Client/Pages/Component/ButtonContainer.razor.css diff --git a/Pages/Component/Dialog.razor b/Server.Client/Pages/Component/Dialog.razor index 673ec2f..673ec2f 100644 --- a/Pages/Component/Dialog.razor +++ b/Server.Client/Pages/Component/Dialog.razor diff --git a/Pages/Component/Dialog.razor.css b/Server.Client/Pages/Component/Dialog.razor.css index 93680c5..93680c5 100644 --- a/Pages/Component/Dialog.razor.css +++ b/Server.Client/Pages/Component/Dialog.razor.css diff --git a/Pages/Component/MediaTagTable.razor b/Server.Client/Pages/Component/MediaTagTable.razor index e687529..0524739 100644 --- a/Pages/Component/MediaTagTable.razor +++ b/Server.Client/Pages/Component/MediaTagTable.razor @@ -1,7 +1,4 @@ -@inject IDbContextFactory<HBContext> dbFactory -@inject ITagService tagService - -<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/> +@inject HBSession hb <table class="data-table"> <tr> @@ -19,7 +16,7 @@ } </td> <td> - <a href="/Gallery?t=@(e.tagDef.Guid)" class="nondecorated"> + <a href="/Gallery?t=@(e.tagDef.TagDefinitionId)" class="nondecorated"> @if(e.isImplicit) { <i>@e.tagDef.Name</i> } else { @@ -40,33 +37,39 @@ @code { [Parameter] - public Media Media { get; set; } + public Guid MediaId { get; set; } - private (TagDefinition tagDef, bool isImplicit)[] tagDefs; + private (TagDefinition tagDef, bool isImplicit)[] tagDefs = []; protected override void OnInitialized() => LoadTagDefs(); public void Refresh() { LoadTagDefs(); - StateHasChanged(); + // StateHasChanged(); } private void Delete(TagDefinition tagDef) { - tagService.RemoveTag(Media.Guid, tagDef.Guid); - Refresh(); + // tagService.RemoveTag(Media.Guid, tagDef.Guid); + // Refresh(); } - private void LoadTagDefs() { - using var db = dbFactory.CreateDbContext(); - var media = db.Media.First(m => m.ObjectId == Media.ObjectId); + private async void LoadTagDefs() { + var tags = await hb.Media.GetTagsAsync(MediaId); + + tagDefs = tags.Select(td => (tagDef: td, isImplicit: false)).ToArray(); + + await InvokeAsync(() => StateHasChanged()); + + // using var db = dbFactory.CreateDbContext(); + // var media = db.Media.First(m => m.ObjectId == Media.ObjectId); - tagDefs = tagService.GetAllTags(Media) - .Where(e => e.tagDefinition.Source == TagSource.UserTag) - .ToArray(); + // tagDefs = tagService.GetAllTags(Media) + // .Where(e => e.tagDefinition.Source == TagSource.UserTag) + // .ToArray(); } private void MakeExplicit(TagDefinition tagDef) { - tagService.AddTag(Media, tagDef); - Refresh(); + // tagService.AddTag(Media, tagDef); + // Refresh(); } } diff --git a/Pages/Component/MediaTagTable.razor.css b/Server.Client/Pages/Component/MediaTagTable.razor.css index 4dedb3f..4dedb3f 100644 --- a/Pages/Component/MediaTagTable.razor.css +++ b/Server.Client/Pages/Component/MediaTagTable.razor.css diff --git a/Pages/Component/MobileMenu.razor b/Server.Client/Pages/Component/MobileMenu.razor index 49c45d5..49c45d5 100644 --- a/Pages/Component/MobileMenu.razor +++ b/Server.Client/Pages/Component/MobileMenu.razor diff --git a/Pages/Component/MobileMenu.razor.css b/Server.Client/Pages/Component/MobileMenu.razor.css index 0237cf1..0237cf1 100644 --- a/Pages/Component/MobileMenu.razor.css +++ b/Server.Client/Pages/Component/MobileMenu.razor.css diff --git a/Server.Client/Pages/Component/NsfwSwitch.razor b/Server.Client/Pages/Component/NsfwSwitch.razor new file mode 100644 index 0000000..f4f9c1f --- /dev/null +++ b/Server.Client/Pages/Component/NsfwSwitch.razor @@ -0,0 +1,6 @@ +<Switch InitialValue=false OnToggle=ToggleNsfw/> + +@code { + private void ToggleNsfw(bool showNsfw) => _ = 0; + // userService.UserSessionState.ShowNsfw = showNsfw; +}
\ No newline at end of file diff --git a/Pages/Component/ProgressBar.razor b/Server.Client/Pages/Component/ProgressBar.razor index aa22194..aa22194 100644 --- a/Pages/Component/ProgressBar.razor +++ b/Server.Client/Pages/Component/ProgressBar.razor diff --git a/Pages/Component/ProgressBar.razor.css b/Server.Client/Pages/Component/ProgressBar.razor.css index f49c982..f49c982 100644 --- a/Pages/Component/ProgressBar.razor.css +++ b/Server.Client/Pages/Component/ProgressBar.razor.css diff --git a/Pages/Component/RedirectLogin.razor b/Server.Client/Pages/Component/RedirectLogin.razor index 290a7ac..290a7ac 100644 --- a/Pages/Component/RedirectLogin.razor +++ b/Server.Client/Pages/Component/RedirectLogin.razor diff --git a/Pages/Component/Switch.razor b/Server.Client/Pages/Component/Switch.razor index d11ac81..5264094 100644 --- a/Pages/Component/Switch.razor +++ b/Server.Client/Pages/Component/Switch.razor @@ -1,6 +1,4 @@ -<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/> - -<label> +<label> <input type="checkbox" checked=@InitialValue diff --git a/Pages/Component/Switch.razor.css b/Server.Client/Pages/Component/Switch.razor.css index 6b1f5d5..6b1f5d5 100644 --- a/Pages/Component/Switch.razor.css +++ b/Server.Client/Pages/Component/Switch.razor.css diff --git a/Pages/Component/TabContainer.razor b/Server.Client/Pages/Component/TabContainer.razor index 3caab0b..3caab0b 100644 --- a/Pages/Component/TabContainer.razor +++ b/Server.Client/Pages/Component/TabContainer.razor diff --git a/Pages/Component/TabContainer.razor.css b/Server.Client/Pages/Component/TabContainer.razor.css index bfb5694..bfb5694 100644 --- a/Pages/Component/TabContainer.razor.css +++ b/Server.Client/Pages/Component/TabContainer.razor.css diff --git a/Pages/Component/TabPane.razor b/Server.Client/Pages/Component/TabPane.razor index ba4a13a..ba4a13a 100644 --- a/Pages/Component/TabPane.razor +++ b/Server.Client/Pages/Component/TabPane.razor diff --git a/Pages/Component/TabPane.razor.css b/Server.Client/Pages/Component/TabPane.razor.css index 5f28270..5f28270 100644 --- a/Pages/Component/TabPane.razor.css +++ b/Server.Client/Pages/Component/TabPane.razor.css diff --git a/Pages/Component/TagEditDialog.razor b/Server.Client/Pages/Component/TagEditDialog.razor index afa312e..6192d55 100644 --- a/Pages/Component/TagEditDialog.razor +++ b/Server.Client/Pages/Component/TagEditDialog.razor @@ -1,5 +1,5 @@ -@inject IDbContextFactory<HBContext> dbFactory; -@inject ITagService tagService +@* @inject IDbContextFactory<HBContext> dbFactory; +@inject ITagService tagService*@ @implements IDialog <Dialog Title=@Title @ref=dialog> @@ -80,9 +80,9 @@ private void Submit() { try { if(TagDefinition is null) { - tagService.CreateTagDefinition(tagName, tagNamespace, tagAlias); + // tagService.CreateTagDefinition(tagName, tagNamespace, tagAlias); } else { - tagService.UpdateTagDefinition(TagDefinition, tagName, tagNamespace, tagAlias); + // tagService.UpdateTagDefinition(TagDefinition, tagName, tagNamespace, tagAlias); } } catch(ApiModels.TagDuplicateException e) { nameExists = e.NameExists; diff --git a/Pages/Component/TagEditDialog.razor.css b/Server.Client/Pages/Component/TagEditDialog.razor.css index 02781c0..02781c0 100644 --- a/Pages/Component/TagEditDialog.razor.css +++ b/Server.Client/Pages/Component/TagEditDialog.razor.css diff --git a/Pages/Component/TagSelectDialog.razor b/Server.Client/Pages/Component/TagSelectDialog.razor index 87065d7..c2fddc2 100644 --- a/Pages/Component/TagSelectDialog.razor +++ b/Server.Client/Pages/Component/TagSelectDialog.razor @@ -1,6 +1,6 @@ -@inject IDbContextFactory<HBContext> dbFactory +@* @inject IDbContextFactory<HBContext> dbFactory @inject ITagService tagService -@inject IUserService userService +@inject IUserService userService*@ @implements IDisposable @implements IDialog @@ -28,7 +28,7 @@ ns, alias is not null ? $"({alias})" : null }); - <input +@* <input type="checkbox" id="tagDef-@tagDefinitions[i].tagDefinition.Guid" @bind=tagDefinitions[local].selected /> @@ -37,7 +37,7 @@ title=@title> @tagDefinitions[i].tagDefinition.Name </label> - } + *@ } </div> <ButtonContainer> <button @onclick=@(() => dialog.Hide()) class="secondary">Cancel</button> @@ -67,7 +67,7 @@ private (TagDefinition tagDefinition, bool selected)[] tagDefinitions; - private HBContext db; + // private HBContext db; private Dialog dialog; @@ -80,33 +80,33 @@ public void Hide() => Visible = false; protected override void OnInitialized() { - userService.UserSessionState.OnStateChange += ShowNsfwChanged; + // userService.UserSessionState.OnStateChange += ShowNsfwChanged; LoadTags(); } private void LoadTags() { - db = dbFactory.CreateDbContext(); + // db = dbFactory.CreateDbContext(); - var selected = SelectedTags.Select(td => td.Guid); + // var selected = SelectedTags.Select(td => td.Guid); - int[] nsfwTags = Array.Empty<int>(); - if(!userService.UserSessionState.ShowNsfw) - nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag) - .Select(td => td.ObjectId) - .ToArray(); + // int[] nsfwTags = Array.Empty<int>(); + // if(!userService.UserSessionState.ShowNsfw) + // nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag) + // .Select(td => td.ObjectId) + // .ToArray(); - tagDefinitions = db.TagDefinitions - .Include(td => td.ImplicitTags) - .Where(td => td.Source == TagSource.UserTag) - .OrderBy(td => td.Name) - .AsEnumerable() - .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags - .IntersectBy(nsfwTags, td => td.ObjectId) - .Any()) - .Select(td => new Tuple<TagDefinition, bool>( - td, - selected.Contains(td.Guid)).ToValueTuple()) - .ToArray(); + // tagDefinitions = db.TagDefinitions + // .Include(td => td.ImplicitTags) + // .Where(td => td.Source == TagSource.UserTag) + // .OrderBy(td => td.Name) + // .AsEnumerable() + // .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags + // .IntersectBy(nsfwTags, td => td.ObjectId) + // .Any()) + // .Select(td => new Tuple<TagDefinition, bool>( + // td, + // selected.Contains(td.Guid)).ToValueTuple()) + // .ToArray(); } private void QueryInput(ChangeEventArgs e) { @@ -141,30 +141,31 @@ } private bool MatchesQuery(TagDefinition tagDef) { - TagDefinition? singleTag = null; + // TagDefinition? singleTag = null; - if(string.IsNullOrEmpty(query)) - return true; + // if(string.IsNullOrEmpty(query)) + // return true; - singleTag = tagDefinitions.FirstOrDefault( - e => string.Equals( - e.tagDefinition.Alias, - query, - StringComparison.OrdinalIgnoreCase)).tagDefinition; + // singleTag = tagDefinitions.FirstOrDefault( + // e => string.Equals( + // e.tagDefinition.Alias, + // query, + // StringComparison.OrdinalIgnoreCase)).tagDefinition; - if(singleTag is not null) - return tagDef.Guid == singleTag.Guid; + // if(singleTag is not null) + // return tagDef.Guid == singleTag.Guid; - singleTag = tagDefinitions.FirstOrDefault( - e => string.Equals( - e.tagDefinition.Name, - query, - StringComparison.OrdinalIgnoreCase)).tagDefinition; + // singleTag = tagDefinitions.FirstOrDefault( + // e => string.Equals( + // e.tagDefinition.Name, + // query, + // StringComparison.OrdinalIgnoreCase)).tagDefinition; - if(singleTag is not null) - return tagDef.Guid == singleTag.Guid; + // if(singleTag is not null) + // return tagDef.Guid == singleTag.Guid; - return tagDef.Name.ToLower().Contains(query.ToLower()); + // return tagDef.Name.ToLower().Contains(query.ToLower()); + return false; } private async void Submit() { @@ -179,14 +180,14 @@ StateHasChanged(); } - public async void ShowNsfwChanged(UserSessionState userSessionState) => - await InvokeAsync(() => { - LoadTags(); - StateHasChanged(); - }); + // public async void ShowNsfwChanged(UserSessionState userSessionState) => + // await InvokeAsync(() => { + // LoadTags(); + // StateHasChanged(); + // }); public void Dispose() { - db.Dispose(); - userService.UserSessionState.OnStateChange -= ShowNsfwChanged; + // db.Dispose(); + // userService.UserSessionState.OnStateChange -= ShowNsfwChanged; } } diff --git a/Pages/Component/TagSelectDialog.razor.css b/Server.Client/Pages/Component/TagSelectDialog.razor.css index dadd0c4..dadd0c4 100644 --- a/Pages/Component/TagSelectDialog.razor.css +++ b/Server.Client/Pages/Component/TagSelectDialog.razor.css diff --git a/Server.Client/Pages/Component/Titlebar.razor b/Server.Client/Pages/Component/Titlebar.razor new file mode 100644 index 0000000..521fb46 --- /dev/null +++ b/Server.Client/Pages/Component/Titlebar.razor @@ -0,0 +1,98 @@ +@inject IJSRuntime jsRuntime +@inject NavigationManager nav +@inject HBSession session; +@* @inject IUserService userService *@ + +<script suppress-error="BL9992"> + async function login() { + var username = document.querySelector('input#username'); + var password = document.querySelector('input#password'); + + var formData = new FormData(); + formData.append('username', username.value); + formData.append('password', password.value); + + var resp = await fetch('/Login', { + method: 'POST', + body: formData + }); + + if(resp.ok) { + window.location.href = '/'; + } else if(resp.status == 403) { + var form = document.querySelector('form.login'); + form.classList.remove('bad-login'); + @* TODO: improve this hacky method of triggering reflow *@ + form.offsetWidth; + form.classList.add('bad-login'); + username.value = password.value = null; + username.focus(); + } else { + alert('Unknown error while attempting to login!'); + } + } + + async function logout() { + var resp = await fetch('/Logout', { method: 'POST' }); + if(resp.ok) { + window.location.href = '/Login'; + } else { + alert('Error logging out!'); + } + } +</script> + +@if(!IsLoginPage) { + <div id="navbar"> + <p class="mobile">HyperBooru</p> + <a class="mobile menu-button" href="javascript:toggleMobileMenu();">☰</a> + + <a class="desktop" href="/">Home</a> + <a class="desktop" href="/TagDefinitions">Tags</a> + <a class="desktop" href="/Gallery?ingest=true">Ingest</a> + <a class="desktop" href="/Upload">Upload</a> + @* <a class="desktop" href="javascript:;" @onclick=@(() => aboutDialog.Show())>About</a> *@ + <a class="desktop" href="javascript:;">About</a> + + <p class="desktop" id="nsfw-label">NSFW</p> + <div id="nsfw-switch" class="desktop"> + <NsfwSwitch/> + </div> + <form action="/Gallery" method="get" class="desktop"> + <input type="text" name="q" placeholder="Search"/> + </form> + <a class="desktop" href="javascript:logout();">Logout</a> + </div> + @* <AboutDialog @ref=aboutDialog/> *@ +} else { + <div id="navbar"> + <h2>Login</h2> + <form class="login" action="javascript:login();"> + <input + id="username" + placeholder="Username" + type="text" + autocorrect="off" + autocapitalize="off" + autocomplete="off" + autofocus + @bind=Username/> + <input id="password" placeholder="Password" type="password" @bind=Password/> + </form> + <a @onclick=Login>Login</a> + </div> +} + +@code { + // private AboutDialog aboutDialog; + + public string Username { get; set; } = ""; + public string Password { get; set; } = ""; + + private bool IsLoginPage => + new Uri(nav.Uri).AbsolutePath.Equals("/Login", StringComparison.OrdinalIgnoreCase); + + private async void Login() { + await session.LoginAsync(Username, Password); + } +} diff --git a/Pages/Component/Titlebar.razor.css b/Server.Client/Pages/Component/Titlebar.razor.css index 58a1c0c..58a1c0c 100644 --- a/Pages/Component/Titlebar.razor.css +++ b/Server.Client/Pages/Component/Titlebar.razor.css diff --git a/Pages/Gallery.razor b/Server.Client/Pages/Gallery.razor index 743485e..b3391d1 100644 --- a/Pages/Gallery.razor +++ b/Server.Client/Pages/Gallery.razor @@ -1,15 +1,13 @@ @page "/" @page "/Gallery" -@inject ITagService tagService -@inject IFeedService feedService -@inject IUserService userService @inject IJSRuntime jsRuntime -@implements IDisposable -@attribute [Authorize] +@inject HBSession hb +@* @implements IDisposable +@attribute [Authorize]*@ <PageTitle>@Title</PageTitle> -@if(Ingest && !userService.UserSessionState.ShowNsfw) { +@if(Ingest && !hb.HasNsfwClaim) { <div id="feed-error"> <p><center>Ingest feed is not available unless NSFW mode is enabled!</center></p> <p><center><i>You must enable NSFW mode to continue...</i></center></p> @@ -21,12 +19,12 @@ </div> } else { <div style="padding:var(--size-default-gap);"> - @foreach(var media in displayMedia) { + @foreach(var mediaId in displayMedia) { // Precalculate thumbnail size to help the browser // lay out the images during initial page load - int width = (int) media.CurrentUploadedFile!.Width! * 200 / (int) media.CurrentUploadedFile.Height!; - <a href="/ViewMedia?m=@(media.Guid)"> - <img src="/media/thumb/@(media.Guid)?h=200" width=@width height="200"/> + @* int width = (int) media.CurrentUploadedFile!.Width! * 200 / (int) media.CurrentUploadedFile.Height!; *@ + <a href="/ViewMedia?m=@mediaId"> + <img src="/media/thumb/@mediaId?h=200" height="200"/> </a> } <div id="canary" style="height:1px;"></div> @@ -77,10 +75,13 @@ } } - private List<Media> displayMedia; + private List<Guid> displayMedia = new(); + + // protected override void OnInitialized() => + // userService.UserSessionState.OnStateChange += ShowNsfwChanged; protected override void OnInitialized() => - userService.UserSessionState.OnStateChange += ShowNsfwChanged; + Console.WriteLine("PENUS"); protected override void OnParametersSet() => LoadMedia(true); @@ -93,7 +94,8 @@ [JSInvokable("LoadMedia")] public void LoadMedia(bool initial = false) { - Media? key = displayMedia?.Any() ?? false && !initial ? displayMedia.Last() : null; + Guid? key = + displayMedia?.Any() ?? false && !initial ? displayMedia.Last() : null; if(initial) displayMedia = new(); @@ -102,40 +104,48 @@ if(Enum.TryParse<ApiModels.SortOrder>(SortOrder, true, out var so)) sortOrder = so; + FeedRequest feedRequest = new FeedRequest() { + SelectIngest = Ingest, + IncludeNsfw = hb.HasNsfwClaim, + ContinuationToken = key, + Count = PageSize, + SortOrder = sortOrder ?? default + }; + if(TagId is not null && Query is null) { - displayMedia!.AddRange(feedService.LoadChunk( - selectIngest: Ingest, - includeNsfw: userService.UserSessionState.ShowNsfw, - tagId: (Guid) TagId!, - key: key, - count: PageSize, - sortOrder: sortOrder ?? default)); + feedRequest = new FeedTagRequest() { + SelectIngest = Ingest, + IncludeNsfw = hb.HasNsfwClaim, + TagId = (Guid) TagId!, + ContinuationToken = key, + Count = PageSize, + SortOrder = sortOrder ?? default + }; } else if(Query is not null && TagId is null) { - displayMedia!.AddRange(feedService.LoadChunk( - selectIngest: Ingest, - includeNsfw: userService.UserSessionState.ShowNsfw, - query: string.IsNullOrWhiteSpace(Query) ? null : Query, - key: key, - count: PageSize, - sortOrder: sortOrder ?? default)); - } else { - displayMedia!.AddRange(feedService.LoadChunk( - selectIngest: Ingest, - includeNsfw: userService.UserSessionState.ShowNsfw, - key: key, - count: PageSize, - sortOrder: sortOrder ?? default)); + feedRequest = new FeedSearchRequest() { + SelectIngest = Ingest, + IncludeNsfw = hb.HasNsfwClaim, + Query = string.IsNullOrWhiteSpace(Query) ? null : Query, + ContinuationToken = key, + Count = PageSize, + SortOrder = sortOrder ?? default + }; } - StateHasChanged(); + hb.Feed + .LoadChunkAsync(feedRequest) + .ContinueWith(async m => { + displayMedia!.AddRange(await m); + await InvokeAsync(() => StateHasChanged()); + }); } - private async void ShowNsfwChanged(UserSessionState userSessionState) { - await InvokeAsync(() => { - LoadMedia(true); - }); - } + // private async void ShowNsfwChanged(UserSessionState userSessionState) { + // await InvokeAsync(() => { + // LoadMedia(true); + // }); + // } - public void Dispose() => - userService.UserSessionState.OnStateChange -= ShowNsfwChanged; + // public void Dispose() => + // userService.UserSessionState.OnStateChange -= ShowNsfwChanged; } diff --git a/Pages/Gallery.razor.css b/Server.Client/Pages/Gallery.razor.css index 6226d9b..6226d9b 100644 --- a/Pages/Gallery.razor.css +++ b/Server.Client/Pages/Gallery.razor.css diff --git a/Server.Client/Pages/Login.razor b/Server.Client/Pages/Login.razor new file mode 100644 index 0000000..bdc6069 --- /dev/null +++ b/Server.Client/Pages/Login.razor @@ -0,0 +1,17 @@ +@page "/Login" +@* @inject NavigationManager navigationManager *@ + +<PageTitle>HyperBooru Login</PageTitle> + +<div/> + +@code { + // [CascadingParameter] + // public Task<AuthenticationState> AuthenticationState{ get; set; } + + protected override void OnInitialized() { + // var authState = AuthenticationState.GetAwaiter().GetResult(); + // if(authState!.User.Identity?.IsAuthenticated ?? false) + // navigationManager.NavigateTo("/"); + } +}
\ No newline at end of file diff --git a/Pages/Login.razor.css b/Server.Client/Pages/Login.razor.css index fc8c8ca..fc8c8ca 100644 --- a/Pages/Login.razor.css +++ b/Server.Client/Pages/Login.razor.css diff --git a/Server.Client/Pages/NotFound.razor b/Server.Client/Pages/NotFound.razor new file mode 100644 index 0000000..917ada1 --- /dev/null +++ b/Server.Client/Pages/NotFound.razor @@ -0,0 +1,5 @@ +@page "/not-found" +@layout MainLayout + +<h3>Not Found</h3> +<p>Sorry, the content you are looking for does not exist.</p>
\ No newline at end of file diff --git a/Pages/TagDefinitions.razor b/Server.Client/Pages/TagDefinitions.razor index f3dca0f..032386e 100644 --- a/Pages/TagDefinitions.razor +++ b/Server.Client/Pages/TagDefinitions.razor @@ -1,9 +1,8 @@ @page "/TagDefinitions" -@inject IDbContextFactory<HBContext> dbFactory +@* @inject IDbContextFactory<HBContext> dbFactory @inject ITagService tagService @inject IUserService userService -@implements IDisposable -@attribute [Authorize] +@implements IDisposable*@ <PageTitle>Tag Definitions</PageTitle> @@ -23,25 +22,26 @@ <th></th> </tr> @foreach(var tagDef in tagDefinitions.Where(td => td.Namespace == ns)) { - <tr data-guid="@tagDef.Guid"> + @* <tr data-guid="@tagDef.Guid"> *@ + <tr> <td>@tagDef.Alias</td> <td> - <a href="/Gallery?t=@tagDef.Guid" class="nondecorated"> +@* <a href="/Gallery?t=@tagDef.Guid" class="nondecorated"> @tagDef.Name </a> - </td> + *@ </td> <td> <i> @{ - var implicitTags = tagDef.ImplicitTags - .Where(td => td.Source == TagSource.UserTag); - foreach(var tag in implicitTags) { - <a href="/Gallery?q=@tag.Name" class="nondecorated"> - @tag.Name - </a> - if(tag != implicitTags.Last()) - @(", ") - } + // var implicitTags = tagDef.ImplicitTags + // .Where(td => td.Source == TagSource.UserTag); + // foreach(var tag in implicitTags) { + // <a href="/Gallery?q=@tag.Name" class="nondecorated"> + // @tag.Name + // </a> + // if(tag != implicitTags.Last()) + // @(", ") + // } } </i> </td> @@ -53,12 +53,12 @@ <a href="javascript:;" @onclick=@(() => PromptImplicitTags(tagDef))> Implicit Tags </a> - @if(tagDef.ImplicitTags.Select(td => td.Guid).Contains(HBContext.NsfwTag)) { +@* @if(tagDef.ImplicitTags.Select(td => td.Guid).Contains(HBContext.NsfwTag)) { <a href="javascript:;" @onclick=@(() => SetNsfw(tagDef, false))>Make SFW</a> } else { <a href="javascript:;" @onclick=@(() => SetNsfw(tagDef, true))>Make NSFW</a> } - </td> + *@ </td> </tr> } </table> @@ -93,35 +93,35 @@ private string?[] tagNamespaces; - protected override void OnInitialized() => - userService.UserSessionState.OnStateChange += ShowNsfwChanged; + // protected override void OnInitialized() => + // userService.UserSessionState.OnStateChange += ShowNsfwChanged; protected override void OnParametersSet() => LoadTags(); private void LoadTags() { - int[] nsfwTags = Array.Empty<int>(); - if(!userService.UserSessionState.ShowNsfw) - nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag) - .Select(td => td.ObjectId) - .ToArray(); + // int[] nsfwTags = Array.Empty<int>(); + // if(!userService.UserSessionState.ShowNsfw) + // nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag) + // .Select(td => td.ObjectId) + // .ToArray(); - tagDefinitions = dbFactory.CreateDbContext().TagDefinitions - .Include(td => td.ImplicitTags) - .Where(td => td.Source == TagSource.UserTag) - .OrderBy(td => td.Namespace) - .ThenBy(td => td.Name) - .AsEnumerable() - .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags - .IntersectBy(nsfwTags, td => td.ObjectId) - .Any()) - .ToArray(); + // tagDefinitions = dbFactory.CreateDbContext().TagDefinitions + // .Include(td => td.ImplicitTags) + // .Where(td => td.Source == TagSource.UserTag) + // .OrderBy(td => td.Namespace) + // .ThenBy(td => td.Name) + // .AsEnumerable() + // .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags + // .IntersectBy(nsfwTags, td => td.ObjectId) + // .Any()) + // .ToArray(); - tagNamespaces = tagDefinitions - .Select(td => td.Namespace) - .Order() - .Distinct() - .ToArray(); + // tagNamespaces = tagDefinitions + // .Select(td => td.Namespace) + // .Order() + // .Distinct() + // .ToArray(); } private void PromptToDelete(TagDefinition toDelete) { @@ -130,13 +130,13 @@ } private void DeleteTagDefinition() { - if(toDelete is null) - return; + // if(toDelete is null) + // return; - tagService.DeleteTagDefinition(toDelete); - deleteTagDialog.Hide(); - LoadTags(); - StateHasChanged(); + // tagService.DeleteTagDefinition(toDelete); + // deleteTagDialog.Hide(); + // LoadTags(); + // StateHasChanged(); } private void PromptTagCreate() { @@ -152,36 +152,36 @@ } private void PromptImplicitTags(TagDefinition toEditImplicit) { - this.toEditImplicit = toEditImplicit; - implicitTagDialog.SelectedTags = - toEditImplicit.ImplicitTags.ToArray(); - implicitTagDialog.Show(); + // this.toEditImplicit = toEditImplicit; + // implicitTagDialog.SelectedTags = + // toEditImplicit.ImplicitTags.ToArray(); + // implicitTagDialog.Show(); } private void SetImplicitTags(TagDefinition[] tagDefs) { if(toEditImplicit is null) return; - tagService.SetImplicitTags(toEditImplicit, tagDefs); + // tagService.SetImplicitTags(toEditImplicit, tagDefs); LoadTags(); StateHasChanged(); } private void SetNsfw(TagDefinition tagDef, bool nsfw) { - if(nsfw) - tagService.AddImplicitTag(tagDef.Guid, HBContext.NsfwTag); - else - tagService.RemoveImplicitTag(tagDef.Guid, HBContext.NsfwTag); - LoadTags(); - StateHasChanged(); + // if(nsfw) + // tagService.AddImplicitTag(tagDef.Guid, HBContext.NsfwTag); + // else + // tagService.RemoveImplicitTag(tagDef.Guid, HBContext.NsfwTag); + // LoadTags(); + // StateHasChanged(); } - private async void ShowNsfwChanged(UserSessionState userSessionState) => - await InvokeAsync(() => { - LoadTags(); - StateHasChanged(); - }); + // private async void ShowNsfwChanged(UserSessionState userSessionState) => + // await InvokeAsync(() => { + // LoadTags(); + // StateHasChanged(); + // }); - public void Dispose() => - userService.UserSessionState.OnStateChange -= ShowNsfwChanged; + // public void Dispose() => + // userService.UserSessionState.OnStateChange -= ShowNsfwChanged; } diff --git a/Pages/TagDefinitions.razor.css b/Server.Client/Pages/TagDefinitions.razor.css index 409eacc..409eacc 100644 --- a/Pages/TagDefinitions.razor.css +++ b/Server.Client/Pages/TagDefinitions.razor.css diff --git a/Pages/Upload.razor b/Server.Client/Pages/Upload.razor index 6d6e8bc..74cfd64 100644 --- a/Pages/Upload.razor +++ b/Server.Client/Pages/Upload.razor @@ -1,5 +1,4 @@ @page "/Upload" -@attribute [Authorize] <div id="dropzone"> <p></p> diff --git a/Pages/Upload.razor.css b/Server.Client/Pages/Upload.razor.css index d510bc6..d510bc6 100644 --- a/Pages/Upload.razor.css +++ b/Server.Client/Pages/Upload.razor.css diff --git a/Pages/ViewMedia.razor b/Server.Client/Pages/ViewMedia.razor index 46cbc45..4c9151e 100644 --- a/Pages/ViewMedia.razor +++ b/Server.Client/Pages/ViewMedia.razor @@ -1,11 +1,12 @@ @page "/ViewMedia" -@using HyperBooru.Util +@* @using HyperBooru.Util*@ +@inject HBSession hb @inject IJSRuntime jsRuntime -@inject IDbContextFactory<HBContext> dbFactory +@* @inject IDbContextFactory<HBContext> dbFactory @inject ITagService tagService @inject IMediaService mediaService @inject ISourceService sourceService -@attribute [Authorize] + *@ <PageTitle>@title</PageTitle> @@ -34,10 +35,12 @@ <div id="hcontainer"> <div id="image-container" class="mobile-pane-image visible"> <img + src="/media/@(MediaId)"/> +@* <img src="/media/@(media.Guid)" width=@media.CurrentUploadedFile.Width height=@media.CurrentUploadedFile.Height/> - </div> + *@ </div> <div id="metadata-show-button"> <a href="javascript:toggleSidebar();" title="Toggle sidebar (S)"></a> </div> @@ -45,7 +48,7 @@ <div id="metadata-container"> <div id="metadata-fileinfo"> @if(infoEditMode) { - <form action="javascript:;" @onsubmit=@(() => ApplyInfoEdit(true))> +@* <form action="javascript:;" @onsubmit=@(() => ApplyInfoEdit(true))> <table id="edit-metadata"> <tr> <td>Title:</td> @@ -57,11 +60,11 @@ </tr> </table> </form> - } else { - <p>Title: <i>@(media.ShortDescription ?? "None")</i></p> - <p class="newlines">Description:<br/><i>@(media.LongDescription ?? "None")</i></p> + *@ } else { + <p>Title: <i>@(media?.ShortDescription ?? "None")</i></p> + <p class="newlines">Description:<br/><i>@(media?.LongDescription ?? "None")</i></p> } - <p>Resolution: @(media.CurrentUploadedFile.Width)x@(media.CurrentUploadedFile.Height)</p> +@* <p>Resolution: @(media.CurrentUploadedFile.Width)x@(media.CurrentUploadedFile.Height)</p> <p class="heading">Upload history</p> <hr/> <table id="uploaded-files" class="data-table"> @@ -102,16 +105,16 @@ </tr> } </table> - </div> + *@ </div> <div id="metadata-tags"> <p class="heading">Tags</p> <hr/> - <MediaTagTable Media=media @ref=mediaTagTable/> + <MediaTagTable MediaId=MediaId @ref=mediaTagTable/> </div> </div> <div id="button-container"> <ButtonContainer> - <button @onclick=@(() => deleteDialog.Show()) class="warning" data-keyboard-shortcut="d"> +@* <button @onclick=@(() => deleteDialog.Show()) class="warning" data-keyboard-shortcut="d"> <img src="/images/trash.svg"/> <p><u>D</u>elete</p> </button> @@ -123,8 +126,8 @@ <img src="/images/book.svg"/> <p>View <u>O</u>CR</p> </button> - @if(infoEditMode) { - <button @onclick=@(() => ApplyInfoEdit(false)) class="secondary"> + *@ @if(infoEditMode) { +@* <button @onclick=@(() => ApplyInfoEdit(false)) class="secondary"> <img src="/images/cross.svg"/> <p>Cancel</p> </button> @@ -132,23 +135,23 @@ <img src="/images/checkmark.svg"/> <p>Apply</p> </button> - } else { + *@ } else { <button @onclick=@(() => InfoEditMode = true) class="secondary" data-keyboard-shortcut="e"> <img src="/images/edit.svg"/> <p><u>E</u>dit Info</p> </button> } - @if(media.IsIngest) { +@* @if(media.IsIngest) { <button @onclick=@(() => SetIngest(false)) data-keyboard-shortcut="c"> <img src="/images/checkmark.svg"/> <p>Mark Tagging <u>C</u>omplete</p> </button> } else { - <button class="secondary" @onclick=@(() => SetIngest(true)) data-keyboard-shortcut="c"> + *@ <button class="secondary" @onclick=@(() => SetIngest(true)) data-keyboard-shortcut="c"> <img src="/images/cross.svg"/> <p>Mark Tagging In<u>c</u>omplete</p> </button> - } + @* } *@ </ButtonContainer> </div> </div> @@ -159,7 +162,7 @@ </div> </div> -<Dialog Title="Delete this media?" @ref=deleteDialog> +@* <Dialog Title="Delete this media?" @ref=deleteDialog> <ButtonContainer> <button @onclick=@(() => deleteDialog.Hide()) class="secondary">Cancel</button> <button @onclick=DeleteMedia class="warning">Confirm</button> @@ -181,56 +184,58 @@ Title="Select one or more tag(s) to add" OnSubmit=AddTags @ref=tagDialog/> - + *@ @code { - [Parameter] - [SupplyParameterFromQuery(Name = "m")] - public Guid MediaId { get; set; } + [Parameter] + [SupplyParameterFromQuery(Name = "m")] + public Guid MediaId { get; set; } - private Media media; + private ApiModels.Media media; - private string title; + private string title; - private bool infoEditMode = false; - private string? shortDescription; - private string? longDescription; + private bool infoEditMode = false; + private string? shortDescription; + private string? longDescription; - private MediaTagTable mediaTagTable; - private Dialog deleteDialog; - private Dialog ocrDialog; - private TagSelectDialog tagDialog; + private MediaTagTable mediaTagTable; + // private Dialog deleteDialog; + // private Dialog ocrDialog; + // private TagSelectDialog tagDialog; - private ElementReference shortDescriptionInput; + private ElementReference shortDescriptionInput; - protected override void OnInitialized() => - LoadMedia(); + protected override void OnInitialized() => + LoadMedia(); - protected override async void OnAfterRender(bool firstRender) { - if(infoEditMode) - await shortDescriptionInput.FocusAsync(); - } + protected override async void OnAfterRender(bool firstRender) { + if(infoEditMode) + await shortDescriptionInput.FocusAsync(); + } - private void LoadMedia() { - using var db = dbFactory.CreateDbContext(); - media = db.Media - .Include(m => m.Tags) - .ThenInclude(t => t.TagDefinition) - .Include(m => m.CurrentUploadedFile) - .Include(m => m.UploadedFiles) - .Include(m => m.OcrData) - .First(m => m.Guid == MediaId); + private async void LoadMedia() { + media = await hb.Media.GetAsync(MediaId); + // using var db = dbFactory.CreateDbContext(); + // media = db.Media + // .Include(m => m.Tags) + // .ThenInclude(t => t.TagDefinition) + // .Include(m => m.CurrentUploadedFile) + // .Include(m => m.UploadedFiles) + // .Include(m => m.OcrData) + // .First(m => m.Guid == MediaId); - title = media.DisplayName ?? "Media View"; - } + // title = media.DisplayName ?? "Media View"; + // InvokeAsync(() => StateHasChanged()); + } - private void AddTags(TagDefinition[] tagDefs) { - foreach(var tagDef in tagDefs) - tagService.AddTag(media, tagDef); - mediaTagTable.Refresh(); - } + // private void AddTags(TagDefinition[] tagDefs) { + // foreach(var tagDef in tagDefs) + // tagService.AddTag(media, tagDef); + // mediaTagTable.Refresh(); + // } private async void SetIngest(bool ingest) { - mediaService.SetIngest(media, ingest); + // mediaService.SetIngest(media, ingest); LoadMedia(); if(ingest) @@ -249,17 +254,17 @@ } } - private void ApplyInfoEdit(bool apply) { - if(apply) { - mediaService.SetDescription(media, shortDescription, longDescription); - LoadMedia(); - } + // private void ApplyInfoEdit(bool apply) { + // if(apply) { + // mediaService.SetDescription(media, shortDescription, longDescription); + // LoadMedia(); + // } - infoEditMode = false; - } + // infoEditMode = false; + // } - private async void DeleteMedia() { - mediaService.Delete(media); - await jsRuntime.InvokeVoidAsync("history.back"); - } + // private async void DeleteMedia() { + // mediaService.Delete(media); + // await jsRuntime.InvokeVoidAsync("history.back"); + // } } diff --git a/Pages/ViewMedia.razor.css b/Server.Client/Pages/ViewMedia.razor.css index 1a856d3..1a856d3 100644 --- a/Pages/ViewMedia.razor.css +++ b/Server.Client/Pages/ViewMedia.razor.css diff --git a/Server.Client/Program.cs b/Server.Client/Program.cs new file mode 100644 index 0000000..21f3e23 --- /dev/null +++ b/Server.Client/Program.cs @@ -0,0 +1,16 @@ +using HyperBooru.ApiClient; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace HyperBooru.Client; + +internal class Program { + static async Task Main(string[] args) { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + + builder.Services.AddSingleton<HBSession>(sp => new(new HttpClient { + BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) + })); + + await builder.Build().RunAsync(); + } +} diff --git a/Server.Client/Routes.razor b/Server.Client/Routes.razor new file mode 100644 index 0000000..105855d --- /dev/null +++ b/Server.Client/Routes.razor @@ -0,0 +1,6 @@ +<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)"> + <Found Context="routeData"> + <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" /> + <FocusOnNavigate RouteData="routeData" Selector="h1" /> + </Found> +</Router> diff --git a/Server.Client/_Imports.razor b/Server.Client/_Imports.razor new file mode 100644 index 0000000..a420c68 --- /dev/null +++ b/Server.Client/_Imports.razor @@ -0,0 +1,13 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using HyperBooru.ApiClient +@using HyperBooru.ApiModels +@using HyperBooru.Client +@using HyperBooru.Client.Layout +@using HyperBooru.Client.Pages.Component diff --git a/Server.Client/wwwroot/appsettings.Development.json b/Server.Client/wwwroot/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Server.Client/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Server.Client/wwwroot/appsettings.json b/Server.Client/wwwroot/appsettings.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Server.Client/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Server/Components/App.razor b/Server/Components/App.razor new file mode 100644 index 0000000..614372e --- /dev/null +++ b/Server/Components/App.razor @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> + <base href="/" /> + <ResourcePreloader /> + <link rel="stylesheet" href="@Assets["app.css"]" /> + <link rel="stylesheet" href="@Assets["HyperBooru.Server.styles.css"]" /> + <link rel="stylesheet" href="/styles/global.css" /> + <link href="/favicon.ico" rel="icon" /> + <link href="/manifest.webmanifest" rel="manifest" /> + <script type="text/javascript" src="/js/dialog.js"></script> + <script type="text/javascript" src="/js/keyboard.js"></script> + <script type="text/javascript" src="/js/mobile.js"></script> + <ImportMap /> + <HeadOutlet @rendermode="InteractiveWebAssembly" /> + </head> + + <body> + <Routes @rendermode="InteractiveWebAssembly" /> + <script src="@Assets["_framework/blazor.web.js"]"></script> + </body> +</html> diff --git a/Server/Components/Pages/Error.razor b/Server/Components/Pages/Error.razor new file mode 100644 index 0000000..576cc2d --- /dev/null +++ b/Server/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +<PageTitle>Error</PageTitle> + +<h1 class="text-danger">Error.</h1> +<h2 class="text-danger">An error occurred while processing your request.</h2> + +@if (ShowRequestId) +{ + <p> + <strong>Request ID:</strong> <code>@RequestId</code> + </p> +} + +<h3>Development Mode</h3> +<p> + Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred. +</p> +<p> + <strong>The Development environment shouldn't be enabled for deployed applications.</strong> + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> + and restarting the app. +</p> + +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/Server/Components/_Imports.razor b/Server/Components/_Imports.razor new file mode 100644 index 0000000..2986fa6 --- /dev/null +++ b/Server/Components/_Imports.razor @@ -0,0 +1,11 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using HyperBooru.Server +@using HyperBooru.Client +@using HyperBooru.Server.Components diff --git a/Controllers/ApiFeedController.cs b/Server/Controllers/ApiFeedController.cs index 382169e..fb260e6 100644 --- a/Controllers/ApiFeedController.cs +++ b/Server/Controllers/ApiFeedController.cs @@ -1,13 +1,11 @@ using HyperBooru.ApiModels; using HyperBooru.Services; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace HyperBooru.Controllers; [ApiController] -[Authorize] [Route("/api/feed")] public class ApiFeedController : Controller { private IFeedService feedService; diff --git a/Controllers/ApiMediaController.cs b/Server/Controllers/ApiMediaController.cs index a1b07b1..5a8ef21 100644 --- a/Controllers/ApiMediaController.cs +++ b/Server/Controllers/ApiMediaController.cs @@ -1,6 +1,5 @@ using HyperBooru.ApiModels; using HyperBooru.Services; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Text.Json; @@ -8,7 +7,6 @@ using System.Text.Json; namespace HyperBooru.Controllers; [ApiController] -[Authorize] [Route("/api/media")] public class ApiMediaController : Controller { private IDbContextFactory<HBContext> dbFactory; diff --git a/Controllers/ApiTagController.cs b/Server/Controllers/ApiTagController.cs index f48cc05..e8417d2 100644 --- a/Controllers/ApiTagController.cs +++ b/Server/Controllers/ApiTagController.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore; namespace HyperBooru.Controllers; [ApiController] -[Authorize] [Route("/api/tag")] public class ApiTagController : Controller { private IDbContextFactory<HBContext> dbFactory; diff --git a/Controllers/ApiUserController.cs b/Server/Controllers/ApiUserController.cs index d678287..d678287 100644 --- a/Controllers/ApiUserController.cs +++ b/Server/Controllers/ApiUserController.cs diff --git a/Controllers/MediaController.cs b/Server/Controllers/MediaController.cs index 6a9e1fc..27c3cbd 100644 --- a/Controllers/MediaController.cs +++ b/Server/Controllers/MediaController.cs @@ -8,7 +8,6 @@ using Microsoft.EntityFrameworkCore; namespace HyperBooru.Controllers; [ApiController] -[Authorize] [Route("/media")] public class MediaController : Controller { private IHttpContextAccessor httpContextAccessor; diff --git a/Dockerfile b/Server/Dockerfile index 7769bf4..7769bf4 100644 --- a/Dockerfile +++ b/Server/Dockerfile diff --git a/ExceptionMiddleware.cs b/Server/ExceptionMiddleware.cs index 29d0e10..29d0e10 100644 --- a/ExceptionMiddleware.cs +++ b/Server/ExceptionMiddleware.cs diff --git a/HBContext.cs b/Server/HBContext.cs index b684a51..b684a51 100644 --- a/HBContext.cs +++ b/Server/HBContext.cs diff --git a/HBObject.cs b/Server/HBObject.cs index 8001ea3..8001ea3 100644 --- a/HBObject.cs +++ b/Server/HBObject.cs diff --git a/Server/LICENSE.txt b/Server/LICENSE.txt new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/Server/LICENSE.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + 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/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. diff --git a/Media.cs b/Server/Media.cs index 2ff9e63..2ff9e63 100644 --- a/Media.cs +++ b/Server/Media.cs diff --git a/Migrations/20260131125650_InitialMigration.Designer.cs b/Server/Migrations/20260131125650_InitialMigration.Designer.cs index 2e4a05e..2e4a05e 100644 --- a/Migrations/20260131125650_InitialMigration.Designer.cs +++ b/Server/Migrations/20260131125650_InitialMigration.Designer.cs diff --git a/Migrations/20260131125650_InitialMigration.cs b/Server/Migrations/20260131125650_InitialMigration.cs index a1a7d8f..a1a7d8f 100644 --- a/Migrations/20260131125650_InitialMigration.cs +++ b/Server/Migrations/20260131125650_InitialMigration.cs diff --git a/Migrations/HBContextModelSnapshot.cs b/Server/Migrations/HBContextModelSnapshot.cs index 422037f..422037f 100644 --- a/Migrations/HBContextModelSnapshot.cs +++ b/Server/Migrations/HBContextModelSnapshot.cs diff --git a/Program.cs b/Server/Program.cs index 5863368..687c6f8 100644 --- a/Program.cs +++ b/Server/Program.cs @@ -1,40 +1,38 @@ +using HyperBooru.ApiClient; +using HyperBooru.Server.Components; using HyperBooru.Services; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.DataProtection; -using Microsoft.AspNetCore.Http.Json; using Microsoft.EntityFrameworkCore; using System.Text.Json.Serialization; -namespace HyperBooru; +namespace HyperBooru.Server; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); - builder.Services.AddSession(); + + // Add services to the container. builder.Services.AddHttpContextAccessor(); - builder.Services.AddAuthentication( - CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); - builder.Services.AddAuthorization(); builder.Services.AddControllers().AddJsonOptions(o => { var converter = new JsonStringEnumConverter(); o.JsonSerializerOptions.Converters.Add(converter); }); - builder.Services.Configure<JsonOptions>(o => { - o.SerializerOptions.TypeInfoResolverChain.Insert(0, new ExceptionJsonResolver()); - }); - builder.Services.AddRazorPages(); - builder.Services.AddServerSideBlazor(); + builder.Services.AddRazorComponents() + .AddInteractiveWebAssemblyComponents(); // Add our custom services builder.Services.AddSingleton<IConfigService, ConfigService>(); builder.Services.AddDbContextFactory<HBContext>(); + builder.Services.AddSingleton<IGlobalUserService, GlobalUserService>(); + builder.Services.AddScoped<IMediaService, MediaService>(); builder.Services.AddScoped<IFeedService, FeedService>(); builder.Services.AddScoped<ITagService, TagService>(); - builder.Services.AddScoped<IMediaService, MediaService>(); - builder.Services.AddSingleton<IGlobalUserService, GlobalUserService>(); builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddHostedService<OcrService>(); builder.Services.AddSingleton<ISourceService, SourceService>(); + builder.Services.AddSingleton(sp => new HBSession() { + BaseUri = new("https://127.0.0.1:7084") + }); // Ensure session keys are stored in a persistent location on all platforms builder.Services.AddDataProtection() @@ -50,17 +48,25 @@ public class Program { using var db = scope.ServiceProvider.GetRequiredService<HBContext>(); db.Database.Migrate(); - app.UseRouting(); - app.UseSession(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseHsts(); + // Configure the HTTP request pipeline. + if(app.Environment.IsDevelopment()) { + app.UseWebAssemblyDebugging(); + } else { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseHttpsRedirection(); - app.UseStaticFiles(); - app.UseMiddleware<ExceptionMiddleware>(); - app.MapBlazorHub(); + + app.UseAntiforgery(); + + app.MapStaticAssets(); app.MapControllers(); - app.MapFallbackToPage("/_Host"); + app.MapRazorComponents<App>() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(Client._Imports).Assembly); app.Run(); } diff --git a/Server/Properties/launchSettings.json b/Server/Properties/launchSettings.json new file mode 100644 index 0000000..f37fc08 --- /dev/null +++ b/Server/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5062", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7084;http://localhost:5062", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/Server.csproj b/Server/Server.csproj index 45bb9bd..829efa4 100644 --- a/Server.csproj +++ b/Server/Server.csproj @@ -1,32 +1,12 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> - <AssemblyName>HyperBooru</AssemblyName> - <RootNamespace>HyperBooru</RootNamespace> - <AssemblyVersion>0.17.0.0</AssemblyVersion> - <FileVersion>$(AssemblyVersion)</FileVersion> - <Version>0.17-alpha</Version> - <UserSecretsId>2907567f-4640-4581-8f4d-0977952d26bd</UserSecretsId> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <NoWarn>1701;1702;8618</NoWarn> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <NoWarn>1701;1702;8618</NoWarn> - </PropertyGroup> - - <PropertyGroup> - <DefaultItemExcludes>$(DefaultItemExcludes);Data/**</DefaultItemExcludes> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)' == 'Release'"> - <DebugType>None</DebugType> - <DebugSymbols>false</DebugSymbols> + <BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException> + <AssemblyName>HyperBooru.Server</AssemblyName> + <RootNamespace>HyperBooru.Server</RootNamespace> </PropertyGroup> <ItemGroup> @@ -37,6 +17,8 @@ </ItemGroup> <ItemGroup> + <ProjectReference Include="..\Server.Client\Client.csproj" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.8" /> <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.13.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7"> @@ -45,13 +27,8 @@ </PackageReference> <PackageReference Include="Mime-Detective" Version="25.8.1" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageReference Include="System.Drawing.Common" Version="10.0.7" /> <PackageReference Include="Tesseract" Version="5.2.0" /> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\ApiModels\ApiModels.csproj" /> - </ItemGroup> - </Project> diff --git a/Services/ConfigService.cs b/Server/Services/ConfigService.cs index ac1f155..ac1f155 100644 --- a/Services/ConfigService.cs +++ b/Server/Services/ConfigService.cs diff --git a/Services/FeedService.cs b/Server/Services/FeedService.cs index 3744e73..3744e73 100644 --- a/Services/FeedService.cs +++ b/Server/Services/FeedService.cs diff --git a/Services/MediaService.cs b/Server/Services/MediaService.cs index e497570..e497570 100644 --- a/Services/MediaService.cs +++ b/Server/Services/MediaService.cs diff --git a/Services/OcrService.cs b/Server/Services/OcrService.cs index d43db2e..d43db2e 100644 --- a/Services/OcrService.cs +++ b/Server/Services/OcrService.cs diff --git a/Services/SourceService.cs b/Server/Services/SourceService.cs index d145346..d145346 100644 --- a/Services/SourceService.cs +++ b/Server/Services/SourceService.cs diff --git a/Services/TagService.cs b/Server/Services/TagService.cs index f7b91dc..f7b91dc 100644 --- a/Services/TagService.cs +++ b/Server/Services/TagService.cs diff --git a/Services/UserService.cs b/Server/Services/UserService.cs index 9e79dc6..9e79dc6 100644 --- a/Services/UserService.cs +++ b/Server/Services/UserService.cs diff --git a/Server/appsettings.Development.json b/Server/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/Server/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/appsettings.json b/Server/appsettings.json index 414e673..414e673 100644 --- a/appsettings.json +++ b/Server/appsettings.json diff --git a/.config/dotnet-tools.json b/Server/dotnet-tools.json index e3d2b85..7dcefc3 100644 --- a/.config/dotnet-tools.json +++ b/Server/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.23", + "version": "10.0.8", "commands": [ "dotnet-ef" ], diff --git a/tessdata/eng.traineddata b/Server/tessdata/eng.traineddata Binary files differindex 176dc32..176dc32 100644 --- a/tessdata/eng.traineddata +++ b/Server/tessdata/eng.traineddata diff --git a/Server/wwwroot/app.css b/Server/wwwroot/app.css new file mode 100644 index 0000000..73a69d6 --- /dev/null +++ b/Server/wwwroot/app.css @@ -0,0 +1,60 @@ +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #006bb7; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +} + +.content { + padding-top: 1.1rem; +} + +h1:focus { + outline: none; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid #e50000; +} + +.validation-message { + color: #e50000; +} + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.darker-border-checkbox.form-check-input { + border-color: #929292; +} + +.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { + color: var(--bs-secondary-color); + text-align: end; +} + +.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { + text-align: start; +}
\ No newline at end of file diff --git a/wwwroot/css/site.css b/Server/wwwroot/css/site.css index 21f9a94..21f9a94 100644 --- a/wwwroot/css/site.css +++ b/Server/wwwroot/css/site.css diff --git a/wwwroot/favicon.ico b/Server/wwwroot/favicon.ico Binary files differindex a1be4cc..a1be4cc 100644 --- a/wwwroot/favicon.ico +++ b/Server/wwwroot/favicon.ico diff --git a/wwwroot/icon-192.png b/Server/wwwroot/icon-192.png Binary files differindex 28ce06d..28ce06d 100644 --- a/wwwroot/icon-192.png +++ b/Server/wwwroot/icon-192.png diff --git a/wwwroot/icon-512.png b/Server/wwwroot/icon-512.png Binary files differindex 8c28696..8c28696 100644 --- a/wwwroot/icon-512.png +++ b/Server/wwwroot/icon-512.png diff --git a/wwwroot/images/book.svg b/Server/wwwroot/images/book.svg index 6cdfc79..6cdfc79 100644 --- a/wwwroot/images/book.svg +++ b/Server/wwwroot/images/book.svg diff --git a/wwwroot/images/checkmark.svg b/Server/wwwroot/images/checkmark.svg index 5e55d9e..5e55d9e 100644 --- a/wwwroot/images/checkmark.svg +++ b/Server/wwwroot/images/checkmark.svg diff --git a/wwwroot/images/cross.svg b/Server/wwwroot/images/cross.svg index 0c37363..0c37363 100644 --- a/wwwroot/images/cross.svg +++ b/Server/wwwroot/images/cross.svg diff --git a/wwwroot/images/edit.svg b/Server/wwwroot/images/edit.svg index d4c6ec4..d4c6ec4 100644 --- a/wwwroot/images/edit.svg +++ b/Server/wwwroot/images/edit.svg diff --git a/wwwroot/images/info.svg b/Server/wwwroot/images/info.svg index b194f05..b194f05 100644 --- a/wwwroot/images/info.svg +++ b/Server/wwwroot/images/info.svg diff --git a/wwwroot/images/loginbg.webp b/Server/wwwroot/images/loginbg.webp Binary files differindex 759e666..759e666 100644 --- a/wwwroot/images/loginbg.webp +++ b/Server/wwwroot/images/loginbg.webp diff --git a/wwwroot/images/photo.svg b/Server/wwwroot/images/photo.svg index 486c360..486c360 100644 --- a/wwwroot/images/photo.svg +++ b/Server/wwwroot/images/photo.svg diff --git a/wwwroot/images/tag.svg b/Server/wwwroot/images/tag.svg index 3eb8843..3eb8843 100644 --- a/wwwroot/images/tag.svg +++ b/Server/wwwroot/images/tag.svg diff --git a/wwwroot/images/trash.svg b/Server/wwwroot/images/trash.svg index 18ff9c1..18ff9c1 100644 --- a/wwwroot/images/trash.svg +++ b/Server/wwwroot/images/trash.svg diff --git a/wwwroot/js/dialog.js b/Server/wwwroot/js/dialog.js index 418962f..418962f 100644 --- a/wwwroot/js/dialog.js +++ b/Server/wwwroot/js/dialog.js diff --git a/wwwroot/js/keyboard.js b/Server/wwwroot/js/keyboard.js index 8b46639..8b46639 100644 --- a/wwwroot/js/keyboard.js +++ b/Server/wwwroot/js/keyboard.js diff --git a/wwwroot/js/mobile.js b/Server/wwwroot/js/mobile.js index 0af11cc..0af11cc 100644 --- a/wwwroot/js/mobile.js +++ b/Server/wwwroot/js/mobile.js diff --git a/wwwroot/loginbg.webm b/Server/wwwroot/loginbg.webm Binary files differindex 139ed0d..139ed0d 100644 --- a/wwwroot/loginbg.webm +++ b/Server/wwwroot/loginbg.webm diff --git a/wwwroot/manifest.webmanifest b/Server/wwwroot/manifest.webmanifest index f150f98..f150f98 100644 --- a/wwwroot/manifest.webmanifest +++ b/Server/wwwroot/manifest.webmanifest diff --git a/wwwroot/styles/data-table.css b/Server/wwwroot/styles/data-table.css index 994d625..994d625 100644 --- a/wwwroot/styles/data-table.css +++ b/Server/wwwroot/styles/data-table.css diff --git a/wwwroot/styles/global.css b/Server/wwwroot/styles/global.css index 9de9fc1..9de9fc1 100644 --- a/wwwroot/styles/global.css +++ b/Server/wwwroot/styles/global.css diff --git a/_Imports.razor b/_Imports.razor deleted file mode 100644 index 4c5566b..0000000 --- a/_Imports.razor +++ /dev/null @@ -1,9 +0,0 @@ -@using HyperBooru -@using HyperBooru.Pages.Component -@using HyperBooru.Services -@using Microsoft.AspNetCore.Authorization -@using Microsoft.AspNetCore.Components.Authorization -@using Microsoft.AspNetCore.Components.Routing -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.EntityFrameworkCore -@using Microsoft.JSInterop diff --git a/appsettings.Development.json b/appsettings.Development.json deleted file mode 100644 index b3da6a4..0000000 --- a/appsettings.Development.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "AllowedHosts": "*", - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://0.0.0.0:7132" - } - } - }, - "DetailedErrors": true, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "HyperBooru.Services.OcrService": "Debug" - } - }, - "DisableOcr": true -} |
