From e326736021be8ff5af4208d16f59d5e3e4f22b3e Mon Sep 17 00:00:00 2001 From: vnugent Date: Wed, 13 Mar 2024 16:19:50 -0400 Subject: Squashed commit of the following: commit 1e08c6d2112459dc02a0ab873123c4a363b01d21 Author: vnugent Date: Wed Mar 13 16:17:58 2024 -0400 ci: verified container build ready for next release commit 85a1e5b7cc5c99e97a2d4e99bbceb0d2139742ff Author: vnugent Date: Tue Mar 12 22:05:16 2024 -0400 ci: exciting bare-metal build process, os support, smaller packages commit 748cdbf4880d830fd794e92856e8c35a46e4f884 Author: vnugent Date: Mon Mar 11 21:21:18 2024 -0400 feat(app): #1 update libs & add curl support --- back-end/src/Endpoints/SiteLookupEndpoint.cs | 156 +++++++++ back-end/src/PlatformFeatures/Curl/CurlResult.cs | 19 ++ back-end/src/PlatformFeatures/Curl/ICurlApp.cs | 34 ++ .../src/PlatformFeatures/Curl/SystemCurlApp.cs | 338 +++++++++++++++++++ .../PlatformFeatures/Curl/WebsiteLookupResult.cs | 23 ++ back-end/src/PlatformFeatures/ISystemApp.cs | 31 ++ back-end/src/SimpleBookmark.csproj | 10 +- back-end/src/SimpleBookmark.json | 13 + back-end/src/SimpleBookmarkEntry.cs | 3 +- ci/config/SimpleBookmark.json | 13 + ci/config/config.json | 34 +- ci/container/Dockerfile | 2 +- ci/container/Taskfile.yaml | 5 +- .../config-templates/SimpleBookmark-template.json | 15 + ci/container/docker-compose.yaml | 4 +- ci/container/run.sh | 2 +- ci/plugins.taskfile.yaml | 39 ++- ci/release.taskfile.yaml | 111 +++++++ ci/setup.sh | 50 --- ci/taskfile.yaml | 89 +++-- front-end/package-lock.json | 370 ++++++++++----------- front-end/package.json | 6 +- front-end/src/buttons.scss | 10 +- front-end/src/components/Bookmarks.vue | 1 - .../src/components/Boomarks/AddOrUpdateForm.vue | 118 +++++-- front-end/src/components/Settings.vue | 8 +- front-end/src/components/Settings/Bookmarks.vue | 84 +++-- front-end/src/components/Settings/PkiSettings.vue | 5 +- front-end/src/components/Settings/Registation.vue | 2 +- front-end/src/main.ts | 4 +- front-end/src/store/bookmarks.ts | 4 +- front-end/src/store/socialMfaPlugin.ts | 74 ++--- front-end/src/store/websiteLookup.ts | 76 +++++ 33 files changed, 1322 insertions(+), 431 deletions(-) create mode 100644 back-end/src/Endpoints/SiteLookupEndpoint.cs create mode 100644 back-end/src/PlatformFeatures/Curl/CurlResult.cs create mode 100644 back-end/src/PlatformFeatures/Curl/ICurlApp.cs create mode 100644 back-end/src/PlatformFeatures/Curl/SystemCurlApp.cs create mode 100644 back-end/src/PlatformFeatures/Curl/WebsiteLookupResult.cs create mode 100644 back-end/src/PlatformFeatures/ISystemApp.cs create mode 100644 ci/release.taskfile.yaml delete mode 100644 ci/setup.sh create mode 100644 front-end/src/store/websiteLookup.ts diff --git a/back-end/src/Endpoints/SiteLookupEndpoint.cs b/back-end/src/Endpoints/SiteLookupEndpoint.cs new file mode 100644 index 0000000..effe6aa --- /dev/null +++ b/back-end/src/Endpoints/SiteLookupEndpoint.cs @@ -0,0 +1,156 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + +using System; +using System.Net; +using System.Text; +using System.Linq; +using System.Threading.Tasks; +using System.Collections.Generic; + +using VNLib.Utils; +using VNLib.Utils.Memory; +using VNLib.Plugins; +using VNLib.Plugins.Essentials; +using VNLib.Plugins.Essentials.Endpoints; +using VNLib.Plugins.Essentials.Extensions; +using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Extensions.Validation; + +using SimpleBookmark.PlatformFeatures.Curl; + +namespace SimpleBookmark.Endpoints +{ + [ConfigurationName("curl")] + internal sealed class SiteLookupEndpoint : ProtectedWebEndpoint + { + const string DefaultCurlExecName = "curl"; + const int MaxTimeoutValue = 30000; + + private readonly SystemCurlApp _curl; + private readonly IAsyncLazy _isSupported; + + public SiteLookupEndpoint(PluginBase plugin, IConfigScope config) + { + string path = config.GetRequiredProperty("path", p => p.GetString()!); + InitPathAndLog(path, plugin.Log); + + string exePath = config.GetValueOrDefault("exe_path", p => p.GetString(), DefaultCurlExecName); + bool httspOnly = config.GetValueOrDefault("https_only", p => p.GetBoolean(), false); + + //Optional extra arguments + string[] extrArgs = config.GetValueOrDefault( + "extra_args", + p => p.EnumerateArray().Select(s => s.GetString()!).ToArray(), + Array.Empty() + ); + + _curl = new SystemCurlApp(exePath, httspOnly, extrArgs); + + //Immediately check if curl is supported + _isSupported = _curl.TestIsAvailable(plugin.UnloadToken).AsLazy(); + } + + protected override async ValueTask GetAsync(HttpEntity entity) + { + WebMessage webm = new(); + + bool isEnabled = await _isSupported; + + //Allow site to cache if curl is supported on the platform + if (entity.QueryArgs.ContainsKey("support")) + { + webm.Success = isEnabled; + return VirtualOk(entity, webm); + } + + //Assert supported value as curl is required for a normal url lookup + if(webm.Assert(isEnabled, "Curl is not supported on the current platform")) + { + return VirtualClose(entity, webm, HttpStatusCode.NotImplemented); + } + + string? url = entity.QueryArgs.GetValueOrDefault("url"); + + if(webm.Assert(!string.IsNullOrWhiteSpace(url), "No url provided")) + { + return VirtualClose(entity, webm, HttpStatusCode.BadRequest); + } + + if(webm.Assert(UrlFromBase64Url(url!, out Uri? uri), "Invalid url provided")) + { + return VirtualClose(entity, webm, HttpStatusCode.UnprocessableEntity); + } + + int? timeoutMs = null; + + //Allow clients to specify a timeout for the request + string? timeoutMsS = entity.QueryArgs.GetValueOrDefault("timeout"); + if (timeoutMsS is not null && int.TryParse(timeoutMsS, out int _timeoutMs)) + { + //Miniumum timeout must be greater than 1 second because curl is timed in seconds + timeoutMs = Math.Clamp(_timeoutMs, 1000, MaxTimeoutValue); + } + + try + { + //Exec curl on the url + CurlResult result = await _curl.ExecLookupAsync(uri!, timeoutMs, entity.EventCancellation); + + if(webm.Assert(result.IsError == false, result.ErrorMessage!)) + { + return VirtualClose(entity, webm, HttpStatusCode.InternalServerError); + } + + webm.Success = true; + webm.Result = result.Result; //Set curl lookup result as the response + + return VirtualOk(entity, webm); + } + catch (TimeoutException) + { + webm.Result = "Request timed out"; + return VirtualClose(entity, webm, HttpStatusCode.InternalServerError); + } + catch (OperationCanceledException) + { + webm.Result = "Request timed out"; + return VirtualClose(entity, webm, HttpStatusCode.InternalServerError); + } + } + + /* + * Reads in a base64url encoded string which is the user's search url and + * attempts to parse it into a uri. If the url is invalid, the function + */ + private static bool UrlFromBase64Url(string base64Url, out Uri? uri) + { + uri = null; + + //Alloc output buffer for decoded data + using UnsafeMemoryHandle output = MemoryUtil.UnsafeAllocNearestPage(base64Url.Length, true); + + ERRNO decoded = VnEncoding.Base64UrlDecode(base64Url, output.Span, Encoding.UTF8); + if(decoded < 1) + { + return false; + } + + //Recover the url string from its binary representation and try to parse it into a uri + string urlstring = Encoding.UTF8.GetString(output.Span[..(int)decoded]); + return Uri.TryCreate(urlstring, UriKind.Absolute, out uri); + } + } +} diff --git a/back-end/src/PlatformFeatures/Curl/CurlResult.cs b/back-end/src/PlatformFeatures/Curl/CurlResult.cs new file mode 100644 index 0000000..7d70e0e --- /dev/null +++ b/back-end/src/PlatformFeatures/Curl/CurlResult.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + +namespace SimpleBookmark.PlatformFeatures.Curl +{ + internal sealed record class CurlResult(WebsiteLookupResult? Result, bool IsError, string? ErrorMessage); +} diff --git a/back-end/src/PlatformFeatures/Curl/ICurlApp.cs b/back-end/src/PlatformFeatures/Curl/ICurlApp.cs new file mode 100644 index 0000000..ec952e0 --- /dev/null +++ b/back-end/src/PlatformFeatures/Curl/ICurlApp.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + + + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace SimpleBookmark.PlatformFeatures.Curl +{ + internal interface ICurlApp + { + /// + /// Executes a lookup on the given website and returns the title and description + /// + /// The website url to search against + /// A token to cancel the operation + /// The result of the website lookup + Task ExecLookupAsync(Uri website, int? timeoutMs, CancellationToken cancellation); + } +} diff --git a/back-end/src/PlatformFeatures/Curl/SystemCurlApp.cs b/back-end/src/PlatformFeatures/Curl/SystemCurlApp.cs new file mode 100644 index 0000000..0949136 --- /dev/null +++ b/back-end/src/PlatformFeatures/Curl/SystemCurlApp.cs @@ -0,0 +1,338 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Diagnostics; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Collections.Generic; + +using VNLib.Utils.Memory; +using VNLib.Utils.Extensions; + +namespace SimpleBookmark.PlatformFeatures.Curl +{ + sealed class SystemCurlApp(string exePath, bool httpsOnly, string[] additionalArgs) : ISystemApp, ICurlApp + { + const int DefaultTimeoutMs = 5000; + + /// + public async Task TestIsAvailable(CancellationToken cancellation) + { + try + { + //Test if the curl application is available on the local system, may be at path + using Process? process = Exec(["--version"]); + + if (process is null) + { + return false; + } + + //Wait for the process to exit + await process.WaitForExitAsync(cancellation); + + //If an ok status code, then we know the curl application is available + return process.ExitCode == 0; + } + //App not found + catch (Win32Exception) + { + return false; + } + } + + private Process? Exec(string[] arguments) + { + ProcessStartInfo startInfo = new() + { + FileName = exePath, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8 + }; + + //Add arguments + arguments.ForEach(startInfo.ArgumentList.Add); + + return Process.Start(startInfo); + } + + private void ValidateUrl(Uri? website) + { + ArgumentNullException.ThrowIfNull(website); + + if (!website.IsAbsoluteUri) + { + throw new ArgumentException("The website url must be an absolute uri", nameof(website)); + } + + if (httpsOnly && website.Scheme != Uri.UriSchemeHttps) + { + throw new ArgumentException("The website url must be an https url only!", nameof(website)); + } + else if (website.Scheme != Uri.UriSchemeHttp && website.Scheme != Uri.UriSchemeHttps) + { + //Http or https only + throw new ArgumentException("The website url must be an http or https url", nameof(website)); + } + } + + /// + public async Task ExecLookupAsync(Uri website, int? timeoutMs, CancellationToken cancellation) + { + //Validate the url + ValidateUrl(website); + + string timeoutArg = timeoutMs.HasValue ? $"{timeoutMs.Value / 1000}" : $"{DefaultTimeoutMs / 1000}"; + + string[] args = [ + "--max-time", timeoutArg, //Set the max time for the request + "-S", //Silent mode is required + "-H", "Accept: text/html,application/html", //Html is required + ..additionalArgs, //Additional global arguments + website.AbsoluteUri + ]; + + //Execute the curl command + + using Process? process = Exec(args); + + if (process is null) + { + return new CurlResult(null, true, "Curl is not enabled on this platform, lookup failed"); + } + + //Parse the html data + Task documentHeadTask = HtmlTokenReader.ReadHeadTokenAsync(process.StandardOutput, cancellation); + + //Respect the user's timeout command and termimate the process if it exceeds the timeout + if (timeoutMs.HasValue) + { + await documentHeadTask.WaitAsync(TimeSpan.FromMilliseconds(timeoutMs.Value)); + + await Task.WhenAll( + DiscardStreamAsync(process.StandardOutput, cancellation), + DiscardStreamAsync(process.StandardError, cancellation) + ).WaitAsync(TimeSpan.FromMilliseconds(timeoutMs.Value)); + } + else + { + await documentHeadTask; + + await Task.WhenAll( + DiscardStreamAsync(process.StandardOutput, cancellation), + DiscardStreamAsync(process.StandardError, cancellation) + ); + } + + await process.WaitForExitAsync(cancellation); + + if (process.ExitCode != 0) + { + return new CurlResult(null, true, "Curl exited with a non-zero status code"); + } + + string? documentHead = await documentHeadTask; + + if (documentHead is null) + { + return new CurlResult(null, true, "Failed to parse html data"); + } + + //Get the lookup result from the document head segmetn + WebsiteLookupResult result = HtmlTokenReader.ParseHtmlData(documentHead); + + return new CurlResult(result, false, null); + } + + /// + /// Safely discards the entire stream of data from the reader without + /// allocating a large string buffer + /// + /// The reader to discard + /// A token to cancel the operation + /// A task that represents the discard opeartion + private static async Task DiscardStreamAsync(TextReader reader, CancellationToken cancellation) + { + using ArrayPoolBuffer discarBuffer = new(8192); + + while (await reader.ReadBlockAsync(discarBuffer.AsMemory(), cancellation) > 0) + { } + } + + private static class HtmlTokenReader + { + /// + /// Gets the document title from the head of the html document + /// + /// The head string containing the title to parse + /// The title string if found + public static string? GetDocTitleFromHead(string head) + { + ReadOnlySpan headChars = head.AsSpan(); + + ReadOnlySpan title = headChars.SliceAfterParam(""); + title = title.SliceBeforeParam(""); + + return title.ToString(); + } + + /// + /// Attempts to get the document summary from the head of the html document + /// in the meta description tag + /// + /// The head string to parse + /// The document description if found + public static string? GetDocumentSummary(string head) + { + ReadOnlySpan headChars = head.AsSpan(); + + ReadOnlySpan desc = headChars.SliceAfterParam(""); + desc = desc.SliceBeforeParam("\">"); + + return desc.ToString(); + } + + /// + /// Attempts to get the document keywords from the head of the html document + /// by parsing the meta keywords tag + /// + /// The document head + /// An array of document keywords found from the head section + public static string[]? GetDocumentKeywords(string head) + { + ReadOnlySpan headChars = head.AsSpan(); + + ReadOnlySpan kwStart = headChars.SliceAfterParam(" kwSpan = kwStart.SliceBeforeParam("\">"); + + List keywords = []; + + //Split the keywords at comma, and remove any empty entries/whitespace + kwSpan.Split(',', keywords, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + return keywords.ToArray(); + } + + public static WebsiteLookupResult ParseHtmlData(string documentHead) + { + //Parse head segments for title, description, and keywords + return new WebsiteLookupResult( + title: GetDocTitleFromHead(documentHead), + description: GetDocumentSummary(documentHead), + keywords: GetDocumentKeywords(documentHead) + ); + } + + + + public static async Task ReadHeadTokenAsync(TextReader reader, CancellationToken cancellation) + { + //String buffer to store parsed head data + StringBuilder stringBuilder = new(1024); + + //Temp copy buffer + using ArrayPoolBuffer buffer = new(4096); + + bool isStart = true, isEnd = false; + + //scan for docuemnt head + do + { + int read = await reader.ReadBlockAsync(buffer.AsMemory(), cancellation); + + if (read == 0) + { + //Read should never return 0, if it does, then there is no head to read + return null; + } + + if (isStart) + { + Memory headSpan = HeadStart(buffer.AsMemory()); + + //No head was found, continue buffering + if (headSpan.IsEmpty) + { + continue; + } + + /* + * Try to find the end of the head, if it is found, then we can break + */ + isEnd = HeadEnd(ref headSpan); + + //Valid head data to buffer + stringBuilder.Append(headSpan); + + isStart = false; + } + else + { + //Head start was already found, just need to buffer until it ends + Memory end = buffer.AsMemory(); + + isEnd = HeadEnd(ref end); + + stringBuilder.Append(end); + + if (isEnd) + { + break; + } + } + + } while (!isEnd); + + return stringBuilder.ToString(); + } + + static Memory HeadStart(Memory start) + { + //find start of head + int headStartIndex = start.Span.IndexOf(""); + + if (headStartIndex == -1) + { + return default; + } + + return start[headStartIndex..]; + } + + static bool HeadEnd(ref Memory end) + { + //find end of head + int headEndIndex = end.Span.IndexOf(""); + + if (headEndIndex == -1) + { + return false; + } + + end = end[..headEndIndex]; + return true; + } + } + } +} diff --git a/back-end/src/PlatformFeatures/Curl/WebsiteLookupResult.cs b/back-end/src/PlatformFeatures/Curl/WebsiteLookupResult.cs new file mode 100644 index 0000000..e9d9bc0 --- /dev/null +++ b/back-end/src/PlatformFeatures/Curl/WebsiteLookupResult.cs @@ -0,0 +1,23 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + +namespace SimpleBookmark.PlatformFeatures.Curl +{ +#pragma warning disable IDE1006 // Naming Styles (JSON serialization) + + internal sealed record class WebsiteLookupResult(string? title, string? description, string[]? keywords); + +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/back-end/src/PlatformFeatures/ISystemApp.cs b/back-end/src/PlatformFeatures/ISystemApp.cs new file mode 100644 index 0000000..11d15f1 --- /dev/null +++ b/back-end/src/PlatformFeatures/ISystemApp.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// 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 . + + +using System.Threading.Tasks; +using System.Threading; + +namespace SimpleBookmark.PlatformFeatures +{ + internal interface ISystemApp + { + /// + /// Gets a value indicating if the curl application is available + /// on the local system. + /// + /// True if the curl exe is available on the local system, false otherwise + Task TestIsAvailable(CancellationToken cancellation); + } +} diff --git a/back-end/src/SimpleBookmark.csproj b/back-end/src/SimpleBookmark.csproj index 609144b..1eeaaba 100644 --- a/back-end/src/SimpleBookmark.csproj +++ b/back-end/src/SimpleBookmark.csproj @@ -34,11 +34,11 @@ - - - - - + + + + + diff --git a/back-end/src/SimpleBookmark.json b/back-end/src/SimpleBookmark.json index 27ebff8..116587d 100644 --- a/back-end/src/SimpleBookmark.json +++ b/back-end/src/SimpleBookmark.json @@ -14,6 +14,19 @@ } }, + //System website lookup endpoint (aka curl) + "curl": { + "path": "/lookup", + "exe_path": "curl", //Path to the curl executable + "extra_args": [ + "--globoff", //Disables unsafe url globbing + "--no-keepalive", //Disables keepalive, uneeded for a single lookup request + "--max-filesize", "100K", //Max file size 100K + "--max-redirs", "5", //Max redirects 5 + "--location", //Follow redirects + ] + }, + "registration": { "path": "/register", //Path for the registration endpoint "token_lifetime_mins": 360, //Token lifetime in minutes diff --git a/back-end/src/SimpleBookmarkEntry.cs b/back-end/src/SimpleBookmarkEntry.cs index a1c9590..13b94a5 100644 --- a/back-end/src/SimpleBookmarkEntry.cs +++ b/back-end/src/SimpleBookmarkEntry.cs @@ -50,9 +50,10 @@ namespace SimpleBookmark //route the bm endpoint this.Route(); this.Route(); + this.Route(); //Ensure database is created after a delay - this.ObserveWork(() => this.EnsureDbCreatedAsync(this), 1000); + this.ObserveWork(() => this.EnsureDbCreatedAsync(this), 1500); Log.Information("Plugin Loaded"); PrintHelloMessage(); diff --git a/ci/config/SimpleBookmark.json b/ci/config/SimpleBookmark.json index 6cb1b93..64be3c1 100644 --- a/ci/config/SimpleBookmark.json +++ b/ci/config/SimpleBookmark.json @@ -14,6 +14,19 @@ } }, + //System website lookup endpoint (aka curl) + "curl": { + "path": "/api/lookup", + "exe_path": "curl", //Path to the curl executable + "extra_args": [ + "--globoff", //Disables unsafe url globbing + "--no-keepalive", //Disables keepalive, uneeded for a single lookup request + "--max-filesize", "100K", //Max file size 100K + "--max-redirs", "5", //Max redirects 5 + "--location", //Follow redirects + ] + }, + "registration": { "path": "/api/register", //Path for the registration endpoint "token_lifetime_mins": 360, //Token lifetime in minutes diff --git a/ci/config/config.json b/ci/config/config.json index e4b33e8..61293b6 100644 --- a/ci/config/config.json +++ b/ci/config/config.json @@ -41,7 +41,7 @@ //Setup the native lib "vnlib.net.compression": { - "lib_path": "lib/vnlib_compress/build/", + "lib_path": "lib/vnlib_compress.dll", "level": 1 }, @@ -95,16 +95,16 @@ //"cors_allowed_authority": [ "localhost:8080" ], //Define a TLS certificate (enables TLS on the interface) - "disabled ssl": { + "ssl": { //Cert may be pem or pfx (include private key in pfx, or include private key in a pem file) - "cert": "/path/to/cert.pfx|pem", + "cert": "ssl/cert.pem", //A pem encoded private key, REQUIRED if using a PEM certificate, may be encrypted with a password - "privkey": "/path/to/private_key.pem", + "privkey": "ssl/key.pem", //An optional password for the ssl private key - "password": "plain-text-password", + //"password": "plain-text-password", //requires that any client connecting to this host present a valid certificate "client_cert_required": false @@ -129,29 +129,29 @@ "assets": "plugins/assets/" }, - "disabled sys_log": { - //"path": "path/to/syslog/file", + "sys_log": { + "path": "data/logs/syslog.txt", //"template": "serilog template for writing to file", - //"flush_sec": 5, - //"retained_files": 31, - //"file_size_limit": 10485760, - //"interval": "infinite" + "flush_sec": 5, + "retained_files": 10, + "file_size_limit": 10485760, + "interval": "infinite" }, "disabled app_log": { - //"path": "path/to/applog/file", + "path": "data/logs/applog.txt", //"template": "serilog template for writing to file", - //"flush_sec": 5, - //"retained_files": 31, - //"file_size_limit": 10485760, - //"interval": "infinite" + "flush_sec": 5, + "retained_files": 10, + "file_size_limit": 10485760, + "interval": "infinite" }, //Sql for the users database "sql": { "debug": false, "provider": "VNLib.Plugins.Extensions.Sql.SQLite.dll", - "source": "simple-bookmark.db" //For sqlite only + "source": "data/simple-bookmark.db" //For sqlite only }, //caching should be setup globally after VNCache #78a47dd diff --git a/ci/container/Dockerfile b/ci/container/Dockerfile index 6804a6e..f5ac798 100644 --- a/ci/container/Dockerfile +++ b/ci/container/Dockerfile @@ -32,7 +32,7 @@ COPY app/ /app #pull compiled libs from build container COPY --from=native-cont /build/out /app/lib -RUN apk update && apk add --no-cache gettext icu-libs dumb-init +RUN apk update && apk add --no-cache gettext icu-libs dumb-init curl #workdir WORKDIR /app diff --git a/ci/container/Taskfile.yaml b/ci/container/Taskfile.yaml index 97548dc..557e48d 100644 --- a/ci/container/Taskfile.yaml +++ b/ci/container/Taskfile.yaml @@ -41,13 +41,12 @@ tasks: cmds: # clean up the run.sh script to remove windows line endings in my wsl default instance - cmd: wsl dos2unix ./run.sh - platform: [ win-x64 ] + platforms: [ windows/amd64 ] #init build image - task: setup-container-image #remove the default config file as it's not needed in the container - - powershell -Command "rm -Force build/app/config.json" - powershell -Command "rm -Force -Recurse build/app/config/" - task: prune-sql-runtimes @@ -84,7 +83,7 @@ tasks: #make build directory - powershell -Command "mkdir build, build/app, build/app/config-templates/, build/app/static/ -Force" #copy the existing linux-x64 build to the build folder, this will be the container base - - powershell -Command "cp -Recurse -Force ../build/linux-x64/* build/app/" + - powershell -Command "cp -Recurse -Force ../build/linux-x86_64/* build/app/" #copy local scripts and config data into the build folder - powershell -Command "cp -Force run.sh, Taskfile.yaml build/app/" - powershell -Command "cp -Force Dockerfile, docker-compose.yaml build/" diff --git a/ci/container/config-templates/SimpleBookmark-template.json b/ci/container/config-templates/SimpleBookmark-template.json index a64a10a..c2bf780 100644 --- a/ci/container/config-templates/SimpleBookmark-template.json +++ b/ci/container/config-templates/SimpleBookmark-template.json @@ -14,6 +14,21 @@ } }, + //System website lookup endpoint (aka curl) + "curl": { + "path": "/api/lookup", + "exe_path": "curl", //Path to the curl executable + "extra_args": [ + "--globoff", //Disables unsafe url globbing + "--no-keepalive", //Disables keepalive, uneeded for a single lookup request + "--max-filesize", + "100K", //Max file size 100K + "--max-redirs", + "5", //Max redirects 5 + "--location", //Follow redirects + ] + }, + "registration": { "path": "/api/register", //Path for the registration endpoint "token_lifetime_mins": ${REG_TOKEN_DURATION_MIN}, //Token lifetime in minutes diff --git a/ci/container/docker-compose.yaml b/ci/container/docker-compose.yaml index eb28055..03eb815 100644 --- a/ci/container/docker-compose.yaml +++ b/ci/container/docker-compose.yaml @@ -27,6 +27,7 @@ services: CACHE_ASM_PATH: "VNLib.Data.Caching.Providers.VNCache.dll" MEMCACHE_ONLY: "true" REDIS_CONNECTION_STRING: "" + #at least one node required if MEMCACHE_ONLY is false VNCACHE_INITIAL_NODES: "[]" #ACCOUNTS MAX_LOGIN_ATTEMPS: "10" @@ -35,6 +36,7 @@ services: PASSWORD_PEPPER: "" DATABASE_PASSWORD: "" REDIS_PASSWORD: "" + #if MEMCACHE_ONLY is false, then the following keys are required to connect to a VNCACHE cluster VNCACHE_CLIENT_PRIVATE_KEY: "" VNCACHE_CACHE_PUBLIC_KEY: "" @@ -42,5 +44,5 @@ services: HTTP_DOWNSTREAM_SERVERS: '[]' #SSL_JSON: '{"cert": "ssl/cert.pem", "privkey":"ssl/priv.pem"}' - SERVER_ARGS: "--input-off" + SERVER_ARGS: "" diff --git a/ci/container/run.sh b/ci/container/run.sh index 2c2636c..c780929 100644 --- a/ci/container/run.sh +++ b/ci/container/run.sh @@ -12,4 +12,4 @@ done cp usr/assets/* plugins/assets/ -rf #start the server -dotnet webserver/VNLib.WebServer.dll --config config/config.json $SERVER_ARGS \ No newline at end of file +dotnet webserver/VNLib.WebServer.dll --config config/config.json --input-off $SERVER_ARGS \ No newline at end of file diff --git a/ci/plugins.taskfile.yaml b/ci/plugins.taskfile.yaml index f39121d..cab3d53 100644 --- a/ci/plugins.taskfile.yaml +++ b/ci/plugins.taskfile.yaml @@ -15,18 +15,18 @@ tasks: all: deps: - - install-accounts - - install-router - - install-sessions - - install-vncache - - install-vncache-sessions - - install-users - - install-sqlite - - install-argon2-lib - - install-compression - - install-compressor-lib - + - install-rpmalloc + - install-compressor-lib + - install-argon2-lib + - install-compression + - install-sqlite cmds: + - task: install-accounts + - task: install-router + - task: install-sessions + - task: install-vncache + - task: install-vncache-sessions + - task: install-users - echo "Installing and configuring plugins and UI" - task: build-bookmarks @@ -156,3 +156,20 @@ tasks: cmd: powershell -Command "rm ./lib/argon2/{{.ITEM}} -Recurse" ignore_error: true + install-rpmalloc: + cmds: + #install the rpmalloc source code package for Linux and Mac + - task: install:install + vars: + PROJECT_NAME: 'vnlib_rpmalloc' + MODULE_NAME: "VNLib.Core" + FILE_NAME: "src.tgz" + DIR: './lib/vnlib_rpmalloc' + + #install the rpmalloc binary for Windows + - task: install:install + vars: + PROJECT_NAME: 'vnlib_rpmalloc' + MODULE_NAME: "VNLib.Core" + FILE_NAME: "win-x64-release-vnlib_rpmalloc.tgz" + DIR: './lib/vnlib_rpmalloc' diff --git a/ci/release.taskfile.yaml b/ci/release.taskfile.yaml new file mode 100644 index 0000000..f6fdf62 --- /dev/null +++ b/ci/release.taskfile.yaml @@ -0,0 +1,111 @@ +# https://taskfile.dev + +#Inlcuded taskfile for object cache server that is used to produce +#ci builds for standalone caching servers + +version: "3" + +vars: + SSL_DIR: "ssl" + DATA_DIR: "data" + DEFAULT_EC_CURVE: "secp384r1" + +tasks: + default: + desc: "Runs the Simple-Bookmark server" + cmds: + - task: run + + run: + desc: "Runs the Simple-Bookmark server" + silent: true + env: + #libraries intentionally do not have extensions, for cross-platform compatibility, the server will load them regardless + VNLIB_SHARED_HEAP_FILE_PATH: lib/vnlib_rpmalloc.dll + VNLIB_ARGON2_DLL_PATH: lib/argon2.dll + cmds: + - cmd: dotnet webserver/VNLib.WebServer.dll --config config/config.json {{.CLI_ARGS}} + + setup-apt: + desc: "Performs initial setup on Debian apt amd64 based machines" + silent: true + cmds: + - apt update + - apt install -y dotnet-runtime-8.0 gcc cmake curl + - task: setup + - echo "Setup complete" + + setup-dnf: + desc: "Performs initial setup on Fedora/Redhat amd (dnf) based machines" + silent: true + cmds: + - dnf update + - dnf install -y dotnet-runtime-8.0 gcc cmake curl + - task: setup + - echo "Setup complete" + + setup-apk: + desc: "Performs initial setup using the APK package manager for amd64 based machines" + silent: true + cmds: + - apk update + - apk add --no-cache dotnet8-runtime build-base cmake curl + - task: setup + - echo "Setup complete" + + setup: + desc: "Performs platform agnostic setup tasks without installing tools (no sudo needed)" + cmds: + #build rpmalloc lib + - task: build-rpmalloc + - task: build-argon2 + - task: build-compress + + #setup ssl dir + - cmd: mkdir ssl/ + platforms: [ linux, darwin ] + ignore_error: true + - cmd: powershell -Command "mkdir ssl/" + platforms: [ windows/amd64 ] + ignore_error: true + + create-cert: + desc: "Genereates a new self-signed TLS certificate" + cmds: + - openssl req -new -x509 -days 365 -keyout {{.SSL_DIR}}/key.pem -out {{.SSL_DIR}}/cert.pem -newkey ec -pkeyopt ec_paramgen_curve:{{.DEFAULT_EC_CURVE}} --nodes + + build-rpmalloc: + internal: true + dir: 'lib/' + cmds: + #build rpmalloc library for linux/mac + - cmd: cd vnlib_rpmalloc/ && task && cp build/libvn_rpmalloc{{if eq OS "darwin"}}.dylib{{else}}.so{{end}} ../vnlib_rpmalloc.dll + platforms: [ linux, darwin ] + + #for windows just copy the existing dll + - cmd: powershell -Command "cp vnlib_rpmalloc/vnlib_rpmalloc.dll vnlib_rpmalloc.dll" + platforms: [ windows/amd64 ] + + build-argon2: + internal: true + dir: 'lib/' + cmds: + #build argon2 library for linux/mac + - cmd: cd argon2/ && task && cp build/libargon2{{if eq OS "darwin"}}.dylib{{else}}.so{{end}} ../argon2.dll + platforms: [ linux, darwin ] + + #for windows just copy the existing dll + - cmd: powershell -Command "cp argon2/argon2.dll argon2.dll" + platforms: [ windows/amd64 ] + + build-compress: + internal: true + dir: 'lib/' + cmds: + - cd vnlib_compress/ && task + #build the native compressor library for linux/mac + - cmd: cd vnlib_compress/ && cp build/libvn_compress{{if eq OS "darwin"}}.dylib{{else}}.so{{end}} ../vnlib_compress.dll + platforms: [ linux, darwin ] + + - cmd: powershell -Command "cp vnlib_compress/build/Release/vnlib_compress.dll vnlib_compress.dll" + platforms: [ windows/amd64 ] \ No newline at end of file diff --git a/ci/setup.sh b/ci/setup.sh deleted file mode 100644 index 0cc153b..0000000 --- a/ci/setup.sh +++ /dev/null @@ -1,50 +0,0 @@ -#! /bin/bash - -echo "Testing for go-task" -#test for platform tools -if ! command -v task &> /dev/null -then - echo "You must install go-task: from https://taskfile.dev/installation/" - exit 1 -fi - -echo "Testing for cmake" -#test for cmake -if ! command -v cmake &> /dev/null -then - echo "You must have cmake installed globally" - exit 1 -fi - -echo "Testing for GNUMake" -#test for make -if ! command -v make &> /dev/null -then - echo "You must have GNUMake installed globally" - exit 1 -fi - -echo "Testing for git" -#test for git -if ! command -v git &> /dev/null -then - echo "You must have git installed globally" - exit 1 -fi - -#build the argon2 native library -pushd argon2 > /dev/null -echo "Building Argon2 native library" -make -argon2_path=$(find "$(pwd)" -iname "libargon2.so.*") - -echo "Add the following environment variable" -echo VNLIB_ARGON2_DLL_PATH=$argon2_path -popd > /dev/null - -#build the vnlib_compress native library -pushd vnlib_compress > /dev/null -echo "Building vnlib_compress native library" -task -echo "Finished building vnlib_compress" -popd > /dev/null \ No newline at end of file diff --git a/ci/taskfile.yaml b/ci/taskfile.yaml index a27b1ac..43e11a8 100644 --- a/ci/taskfile.yaml +++ b/ci/taskfile.yaml @@ -7,6 +7,7 @@ version: "3" vars: BUILDS_URL: https://www.vaughnnugent.com/public/resources/software/builds + SQLITE_OUT_DIR: "plugins/assets/VNLib.Plugins.Extensions.Loading.Sql.SQLite" includes: install: @@ -30,22 +31,33 @@ tasks: - cmd: powershell -Command "rm -Recurse -Force ./dist" ignore_error: true - #copy setup script for linux - cmd: powershell -Command "mkdir lib -Force" ignore_error: true - - cmd: wsl dos2unix ./setup.sh #convert the setup script to unix line endings for linux - platform: [ win-x64 ] - - powershell -Command "cp setup.sh lib/ -Force" - - task: install-plugins + - task: plugins:all - task: install-webserver + - task: prune-runtimes #run container build last - task: container:build install-webserver: cmds: - - for: [ win-x64, linux-x64, osx-x64 ] + - cmd : powershell -Command "mkdir webserver -Force" + ignore_error: true + + #clone the webserver (it's cross platform when using dotnet command) + - task: install:install + vars: + PROJECT_NAME: 'VNLib.Webserver' + MODULE_NAME: "VNLib.Webserver" + FILE_NAME: "linux-x64-release.tgz" + DIR: 'webserver/' + + #remove the executable since its not needed + - cmd: cd webserver/ && powershell -Command "rm VNlib.WebServer" + + - for: [ windows-x86_64, linux-x86_64, osx-x86_64, windows-arm, linux-arm, osx-arm ] task: create-env vars: TARGET_OS: '{{.ITEM}}' @@ -55,7 +67,7 @@ tasks: #make bin dir - cmd: powershell -Command "mkdir bin -Force" ignore_error: true - - for: [ win-x64, linux-x64, osx-x64 ] + - for: [ windows-x86_64, linux-x86_64, osx-x86_64, windows-arm, linux-arm, osx-arm ] task: pack vars: TARGET_OS: '{{.ITEM}}' @@ -66,11 +78,6 @@ tasks: ignore_error: true - task: container:postbuild_success - - install-plugins: - cmds: - #add plugins - - task: plugins:all build-container: cmds: @@ -84,39 +91,57 @@ tasks: - cmd: powershell -Command "mkdir {{.BUILD_DIR}} -Force" ignore_error: true - #copy build files - - for: [ plugins, dist, lib, config ] + #copy build files for target os + - for: [ plugins, dist, lib, config, webserver ] cmd: powershell -Command "cp -Recurse -Force {{.ITEM}} {{.BUILD_DIR}}" - - task: get-webserver - vars: - TARGET_OS: '{{.TARGET_OS}}' - BUILD_DIR: '{{.BUILD_DIR}}' - - get-webserver: - internal: true - cmds: - - task: install:install - vars: - PROJECT_NAME: 'VNLib.Webserver' - MODULE_NAME: "VNLib.Webserver" - FILE_NAME: "{{.TARGET_OS}}-release.tgz" - DIR: '{{.BUILD_DIR}}/webserver' - - - cmd: powershell -Command "cp -Force ./config/config.json {{.BUILD_DIR}}/config.json" + #copy release taskfile and rename it + - cmd: powershell -Command "cp -Force release.taskfile.yaml {{.BUILD_DIR}}/Taskfile.yaml" pack: internal: true cmds: - cmd: powershell -Command "mkdir build/{{.TARGET_OS}}/ -Force" ignore_error: true - - cd build/{{.TARGET_OS}} && tar -czf ../../bin/{{.TARGET_OS}}-release.tgz . + - cd build/{{.TARGET_OS}} && tar -czf ../../bin/{{.TARGET_OS}}-release.tgz . + + prune-runtimes: + cmds: + #prune sqlite runtime native libraries that Im not targeting + #windows + - for: ['browser-wasm', 'linux-arm', 'linux-arm64', 'linux-armel', 'linux-mips64', 'linux-musl-arm', 'linux-musl-arm64', 'linux-musl-x64', 'linux-ppc64le', 'linux-s390x', 'linux-x64', 'linux-x86', 'maccatalyst-arm64', 'maccatalyst-x64', 'osx-arm64', 'osx-x64', 'win-arm', 'win-arm64' ] + cmd: cd build/windows-x86_64/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true + + #windows arm + - for: ['browser-wasm', 'linux-arm', 'linux-arm64', 'linux-armel', 'linux-mips64', 'linux-musl-arm', 'linux-musl-arm64', 'linux-musl-x64', 'linux-ppc64le', 'linux-s390x', 'linux-x64', 'linux-x86', 'maccatalyst-arm64', 'maccatalyst-x64', 'osx-arm64', 'osx-x64', 'win-x86', 'win-x64' ] + cmd: cd build/windows-arm/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true + + #linux x64 + - for: ['browser-wasm', 'linux-arm', 'linux-arm64', 'linux-armel', 'linux-musl-arm', 'linux-musl-arm64', 'maccatalyst-arm64', 'maccatalyst-x64', 'osx-arm64', 'osx-x64', 'win-arm', 'win-arm64', 'win-x86', 'win-x64' ] + cmd: cd build/linux-x86_64/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true + #linux arm + - for: ['browser-wasm', 'linux-mips64', 'linux-musl-x64', 'linux-ppc64le', 'linux-s390x', 'linux-x64', 'linux-x86', 'maccatalyst-arm64', 'maccatalyst-x64', 'osx-arm64', 'osx-x64', 'win-arm', 'win-arm64', 'win-x86', 'win-x64' ] + cmd: cd build/linux-arm/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true + + #osx x64 + - for: ['browser-wasm', 'linux-arm', 'linux-arm64', 'linux-armel', 'linux-mips64', 'linux-musl-arm', 'linux-musl-arm64', 'linux-musl-x64', 'linux-ppc64le', 'linux-s390x', 'linux-x64', 'linux-x86', 'maccatalyst-arm64', 'win-arm', 'win-arm64', 'win-x86', 'win-x64' ] + cmd: cd build/osx-x86_64/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true + + #osx arm + - for: ['browser-wasm', 'linux-arm', 'linux-arm64', 'linux-armel', 'linux-mips64', 'linux-musl-arm', 'linux-musl-arm64', 'linux-musl-x64', 'linux-ppc64le', 'linux-s390x', 'linux-x64', 'linux-x86', 'maccatalyst-x64', 'osx-x64', 'win-arm', 'win-arm64', 'win-x86', 'win-x64' ] + cmd: cd build/osx-arm/{{.SQLITE_OUT_DIR}}/runtimes && powershell -Command "rm {{.ITEM}} -Recurse -Force" + ignore_error: true clean: ignore_error: true cmds: - - for: [ ./build, ./bin, ./dist, ./plugins, ./lib ] + - for: [ build/, bin/, dist/, plugins/, lib/, webserver/ ] cmd: powershell -Command "rm -Recurse -Force '{{.ITEM}}'" - task: container:clean \ No newline at end of file diff --git a/front-end/package-lock.json b/front-end/package-lock.json index 9231ae5..856b959 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -10,7 +10,7 @@ "license": "agpl3", "dependencies": { "@headlessui/vue": "^1.7.17", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/eb9752ab262522271ccaf1ff127658b7202289a4/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/f2ac807486a00db4ba8486133d567e392f0fe98a/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.2", "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", @@ -62,9 +62,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -588,14 +588,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", - "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -611,9 +611,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -625,9 +625,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", - "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -686,9 +686,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.1.tgz", + "integrity": "sha512-iU2Sya8hNn1LhsYyf0N+L4Gf9Qc+9eBTJJJsaOGUp+7x4n2M9dxTt8UvhJl3oeftSjblSlpCfvjA/IfP3g5VjQ==", "cpu": [ "arm" ], @@ -699,9 +699,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.1.tgz", + "integrity": "sha512-wlzcWiH2Ir7rdMELxFE5vuM7D6TsOcJ2Yw0c3vaBR3VOsJFVTx9xvwnAvhgU5Ii8Gd6+I11qNHwndDscIm0HXg==", "cpu": [ "arm64" ], @@ -712,9 +712,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.1.tgz", + "integrity": "sha512-YRXa1+aZIFN5BaImK+84B3uNK8C6+ynKLPgvn29X9s0LTVCByp54TB7tdSMHDR7GTV39bz1lOmlLDuedgTwwHg==", "cpu": [ "arm64" ], @@ -725,9 +725,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.1.tgz", + "integrity": "sha512-opjWJ4MevxeA8FhlngQWPBOvVWYNPFkq6/25rGgG+KOy0r8clYwL1CFd+PGwRqqMFVQ4/Qd3sQu5t7ucP7C/Uw==", "cpu": [ "x64" ], @@ -738,9 +738,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.1.tgz", + "integrity": "sha512-uBkwaI+gBUlIe+EfbNnY5xNyXuhZbDSx2nzzW8tRMjUmpScd6lCQYKY2V9BATHtv5Ef2OBq6SChEP8h+/cxifQ==", "cpu": [ "arm" ], @@ -751,9 +751,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.1.tgz", + "integrity": "sha512-0bK9aG1kIg0Su7OcFTlexkVeNZ5IzEsnz1ept87a0TUgZ6HplSgkJAnFpEVRW7GRcikT4GlPV0pbtVedOaXHQQ==", "cpu": [ "arm64" ], @@ -764,9 +764,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.1.tgz", + "integrity": "sha512-qB6AFRXuP8bdkBI4D7UPUbE7OQf7u5OL+R94JE42Z2Qjmyj74FtDdLGeriRyBDhm4rQSvqAGCGC01b8Fu2LthQ==", "cpu": [ "arm64" ], @@ -777,9 +777,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.1.tgz", + "integrity": "sha512-sHig3LaGlpNgDj5o8uPEoGs98RII8HpNIqFtAI8/pYABO8i0nb1QzT0JDoXF/pxzqO+FkxvwkHZo9k0NJYDedg==", "cpu": [ "riscv64" ], @@ -790,9 +790,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.1.tgz", + "integrity": "sha512-nD3YcUv6jBJbBNFvSbp0IV66+ba/1teuBcu+fBBPZ33sidxitc6ErhON3JNavaH8HlswhWMC3s5rgZpM4MtPqQ==", "cpu": [ "x64" ], @@ -803,9 +803,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.1.tgz", + "integrity": "sha512-7/XVZqgBby2qp/cO0TQ8uJK+9xnSdJ9ct6gSDdEr4MfABrjTyrW6Bau7HQ73a2a5tPB7hno49A0y1jhWGDN9OQ==", "cpu": [ "x64" ], @@ -816,9 +816,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.1.tgz", + "integrity": "sha512-CYc64bnICG42UPL7TrhIwsJW4QcKkIt9gGlj21gq3VV0LL6XNb1yAdHVp1pIi9gkts9gGcT3OfUYHjGP7ETAiw==", "cpu": [ "arm64" ], @@ -829,9 +829,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.1.tgz", + "integrity": "sha512-LN+vnlZ9g0qlHGlS920GR4zFCqAwbv2lULrR29yGaWP9u7wF5L7GqWu9Ah6/kFZPXPUkpdZwd//TNR+9XC9hvA==", "cpu": [ "ia32" ], @@ -842,9 +842,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.1.tgz", + "integrity": "sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==", "cpu": [ "x64" ], @@ -855,20 +855,20 @@ ] }, "node_modules/@tanstack/virtual-core": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.2.tgz", - "integrity": "sha512-DATZJs8iejkIUqXZe6ruDAnjFo78BKnIIgqQZrc7CmEFqfLEN/TPD91n4hRfo6hpRB6xC00bwKxv7vdjFNEmOg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.1.3.tgz", + "integrity": "sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/vue-virtual": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.1.2.tgz", - "integrity": "sha512-RmUnhsFtRw9p4Ti/+rG2Hr3y4yFhs8Xdsn7x9tkPoKINbVya/5RSCoNUCCAg2iXNjOI5a55iBNzNV0SVwxMwKA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.1.3.tgz", + "integrity": "sha512-OoRCSgp8Bc85Te3pg4OHFUukbWZeB25/O5rNd7MgMtrYIfJjNOaicZeJcvwqK6lDVTMpzohWUMVK/loqR1H8ig==", "dependencies": { - "@tanstack/virtual-core": "3.1.2" + "@tanstack/virtual-core": "3.1.3" }, "funding": { "type": "github", @@ -930,8 +930,8 @@ }, "node_modules/@vnuge/vnlib.browser": { "version": "0.1.13", - "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/eb9752ab262522271ccaf1ff127658b7202289a4/@vnuge-vnlib.browser/release.tgz", - "integrity": "sha512-yqJXL0H8g27KoCijlPXSG75ZxfWab4cFVdT2t2b+iodpHeytZHemlNteTubzMurA8WRg95WW3Z3mf5R184UnZA==", + "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/f2ac807486a00db4ba8486133d567e392f0fe98a/@vnuge-vnlib.browser/release.tgz", + "integrity": "sha512-j3BwCdXWJ46Q7GohS+rZg7M5k1/AS+uuycP7wY8RWI2YBKS80uTE6jbWZ0OuCybclBrCufvW7SlTTpfsbf33mw==", "license": "MIT", "peerDependencies": { "@vueuse/core": "^10.x", @@ -940,8 +940,7 @@ "jose": "^5.x", "lodash-es": "^4.x", "universal-cookie": "^7.0.x", - "vue": "^3.x", - "vue-router": "^4.x" + "vue": "^3.x" } }, "node_modules/@volar/language-core": { @@ -973,49 +972,49 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz", - "integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", + "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", "dependencies": { "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.19", + "@vue/shared": "3.4.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz", - "integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", + "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", "dependencies": { - "@vue/compiler-core": "3.4.19", - "@vue/shared": "3.4.19" + "@vue/compiler-core": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz", - "integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", + "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", "dependencies": { "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.19", - "@vue/compiler-dom": "3.4.19", - "@vue/compiler-ssr": "3.4.19", - "@vue/shared": "3.4.19", + "@vue/compiler-core": "3.4.21", + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21", "estree-walker": "^2.0.2", - "magic-string": "^0.30.6", - "postcss": "^8.4.33", + "magic-string": "^0.30.7", + "postcss": "^8.4.35", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz", - "integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", + "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", "dependencies": { - "@vue/compiler-dom": "3.4.19", - "@vue/shared": "3.4.19" + "@vue/compiler-dom": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/devtools-api": { @@ -1073,48 +1072,48 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz", - "integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", + "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", "dependencies": { - "@vue/shared": "3.4.19" + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz", - "integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", + "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", "dependencies": { - "@vue/reactivity": "3.4.19", - "@vue/shared": "3.4.19" + "@vue/reactivity": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz", - "integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", + "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", "dependencies": { - "@vue/runtime-core": "3.4.19", - "@vue/shared": "3.4.19", + "@vue/runtime-core": "3.4.21", + "@vue/shared": "3.4.21", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz", - "integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", + "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", "dependencies": { - "@vue/compiler-ssr": "3.4.19", - "@vue/shared": "3.4.19" + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { - "vue": "3.4.19" + "vue": "3.4.21" } }, "node_modules/@vue/shared": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz", - "integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==" + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, "node_modules/@vuelidate/core": { "version": "2.0.3", @@ -1201,13 +1200,13 @@ } }, "node_modules/@vueuse/core": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.8.0.tgz", - "integrity": "sha512-G9Ok9fjx10TkNIPn8V1dJmK1NcdJCtYmDRyYiTMUyJ1p0Tywc1zmOoCQ2xhHYyz8ULBU4KjIJQ9n+Lrty74iVw==", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz", + "integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.8.0", - "@vueuse/shared": "10.8.0", + "@vueuse/metadata": "10.9.0", + "@vueuse/shared": "10.9.0", "vue-demi": ">=0.14.7" }, "funding": { @@ -1240,17 +1239,17 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.8.0.tgz", - "integrity": "sha512-Nim/Vle5OgXcXhAvGOgkJQXB1Yb+Kq/fMbLuv3YYDYbiQrwr39ljuD4k9fPeq4yUyokYRo2RaNQmbbIMWB/9+w==", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz", + "integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.8.0.tgz", - "integrity": "sha512-dUdy6zwHhULGxmr9YUg8e+EnB39gcM4Fe2oKBSrh3cOsV30JcMPtsyuspgFCUo5xxFNaeMf/W2yyKfST7Bg8oQ==", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz", + "integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==", "dependencies": { "vue-demi": ">=0.14.7" }, @@ -1377,9 +1376,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.18", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", + "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", "dev": true, "funding": [ { @@ -1396,8 +1395,8 @@ } ], "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001591", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -1521,9 +1520,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001589", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz", - "integrity": "sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==", + "version": "1.0.30001597", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz", + "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==", "dev": true, "funding": [ { @@ -1757,9 +1756,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.681", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.681.tgz", - "integrity": "sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg==", + "version": "1.4.700", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.700.tgz", + "integrity": "sha512-40dqKQ3F7C8fbBEmjSeJ+qEHCKzPyrP9SkeIBZ3wSCUH9nhWStrDz030XlDzlhNhlul1Z0fz7TpDFnsIzo4Jtg==", "dev": true }, "node_modules/emoji-regex": { @@ -2254,9 +2253,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -2433,9 +2432,9 @@ } }, "node_modules/jose": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.2.tgz", - "integrity": "sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", + "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -2560,9 +2559,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -3224,9 +3223,9 @@ } }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.1.tgz", + "integrity": "sha512-ggqQKvx/PsB0FaWXhIvVkSWh7a/PCLQAsMjBc+nA2M8Rv2/HG0X6zvixAB7KyZBRtifBUhy5k8voQX/mRnABPg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3239,19 +3238,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.12.1", + "@rollup/rollup-android-arm64": "4.12.1", + "@rollup/rollup-darwin-arm64": "4.12.1", + "@rollup/rollup-darwin-x64": "4.12.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.1", + "@rollup/rollup-linux-arm64-gnu": "4.12.1", + "@rollup/rollup-linux-arm64-musl": "4.12.1", + "@rollup/rollup-linux-riscv64-gnu": "4.12.1", + "@rollup/rollup-linux-x64-gnu": "4.12.1", + "@rollup/rollup-linux-x64-musl": "4.12.1", + "@rollup/rollup-win32-arm64-msvc": "4.12.1", + "@rollup/rollup-win32-ia32-msvc": "4.12.1", + "@rollup/rollup-win32-x64-msvc": "4.12.1", "fsevents": "~2.3.2" } }, @@ -3656,9 +3655,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -3723,9 +3722,9 @@ "dev": true }, "node_modules/vite": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", - "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", "dev": true, "dependencies": { "esbuild": "^0.19.3", @@ -3778,15 +3777,15 @@ } }, "node_modules/vue": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz", - "integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", + "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", "dependencies": { - "@vue/compiler-dom": "3.4.19", - "@vue/compiler-sfc": "3.4.19", - "@vue/runtime-dom": "3.4.19", - "@vue/server-renderer": "3.4.19", - "@vue/shared": "3.4.19" + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-sfc": "3.4.21", + "@vue/runtime-dom": "3.4.21", + "@vue/server-renderer": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { "typescript": "*" @@ -3821,21 +3820,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/vue-router": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz", - "integrity": "sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==", - "peer": true, - "dependencies": { - "@vue/devtools-api": "^6.5.1" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, "node_modules/vue-template-compiler": { "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", @@ -3864,9 +3848,9 @@ } }, "node_modules/vue3-otp-input": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/vue3-otp-input/-/vue3-otp-input-0.4.1.tgz", - "integrity": "sha512-wVl9i3DcWlO0C7fBI9V+RIP3crm/1tY72fuhvb3YM2JfbLoYofB96aPl5AgFhA0Cse5bQEMYtIvOeiqW3rfbAw==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/vue3-otp-input/-/vue3-otp-input-0.4.4.tgz", + "integrity": "sha512-LI1MeBiiEy59cnjqXzlcz4G4cMxZcHF/xOKilb6sfw4uFHfQ22Luu2ls0Bb51zL0pb3gGp7RuIL5eurEJXkoBg==", "engines": { "node": ">=16.0.0", "npm": ">=8.0.0" @@ -3996,9 +3980,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", - "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", "dev": true, "bin": { "yaml": "bin.mjs" diff --git a/front-end/package.json b/front-end/package.json index 30accd8..0631eae 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -14,11 +14,13 @@ "dev": "vite", "watch": "vite build --watch --mode development --minify false", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "update": "npm update", + "oudated": "npm outdated" }, "dependencies": { "@headlessui/vue": "^1.7.17", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/eb9752ab262522271ccaf1ff127658b7202289a4/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/f2ac807486a00db4ba8486133d567e392f0fe98a/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.2", "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", diff --git a/front-end/src/buttons.scss b/front-end/src/buttons.scss index 7088deb..44df2c2 100644 --- a/front-end/src/buttons.scss +++ b/front-end/src/buttons.scss @@ -1,5 +1,13 @@ .btn{ - @apply focus:ring-2 focus:outline-none font-medium rounded text-sm px-4 py-2 text-center text-white; + @apply focus:ring-2 focus:outline-none font-medium rounded text-sm px-2.5 py-2 text-center text-white; + + &.sm{ + @apply text-xs px-2 py-1; + } + + &.lg{ + @apply text-lg px-4 py-3; + } &.round{ @apply rounded-full; diff --git a/front-end/src/components/Bookmarks.vue b/front-end/src/components/Bookmarks.vue index cc3cd6a..274b0b4 100644 --- a/front-end/src/components/Bookmarks.vue +++ b/front-end/src/components/Bookmarks.vue @@ -387,7 +387,6 @@ const upload = (() => { Search -
diff --git a/front-end/src/components/Boomarks/AddOrUpdateForm.vue b/front-end/src/components/Boomarks/AddOrUpdateForm.vue index a4a3f1d..0370e0c 100644 --- a/front-end/src/components/Boomarks/AddOrUpdateForm.vue +++ b/front-end/src/components/Boomarks/AddOrUpdateForm.vue @@ -1,6 +1,8 @@