aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-08-17 22:35:53 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-08-17 22:35:53 -0400
commitac67c472f9f70cc60e749283a9a6fc32e5f65fe6 (patch)
tree985d0b921ed8912cdbf9a1402e7422102956bf5a
parent85540afac8a5ba822d845039c8a61fe48b3a1b6a (diff)
update glance widget
-rw-r--r--back-end/src/Endpoints/WidgetEndpoint.cs123
1 files changed, 99 insertions, 24 deletions
diff --git a/back-end/src/Endpoints/WidgetEndpoint.cs b/back-end/src/Endpoints/WidgetEndpoint.cs
index d72030b..ad2fa39 100644
--- a/back-end/src/Endpoints/WidgetEndpoint.cs
+++ b/back-end/src/Endpoints/WidgetEndpoint.cs
@@ -1,4 +1,4 @@
-W// Copyright (C) 2024 Vaughn Nugent
+// 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
@@ -13,39 +13,37 @@
// 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/>.
+using System;
using System.Net;
using System.Threading.Tasks;
+using System.Text;
+using VNLib.Utils.IO;
+using VNLib.Utils.Memory;
+using VNLib.Utils.Logging;
+using VNLib.Utils.Extensions;
+using VNLib.Net.Http;
using VNLib.Plugins;
using VNLib.Plugins.Essentials;
using VNLib.Plugins.Essentials.Endpoints;
using VNLib.Plugins.Extensions.Loading;
using VNLib.Plugins.Essentials.Extensions;
+using VNLib.Plugins.Extensions.Loading.Routing;
using SimpleBookmark.Model;
using SimpleBookmark.Model.Widget;
namespace SimpleBookmark.Endpoints
{
+ [EndpointPath("{{path}}")]
+ [EndpointLogName("widget-endpoint")]
[ConfigurationName("widgets")]
- internal sealed class WidgetEndpoint : UnprotectedWebEndpoint
+ internal sealed class WidgetEndpoint(PluginBase plugin, IConfigScope config) : UnprotectedWebEndpoint
{
- private readonly BookmarkStore bookmarks;
- private readonly WidgetAuthManager authManager;
- private readonly bool Enabled;
- private readonly string? CorsAclHeaerDomains;
-
- public WidgetEndpoint(PluginBase plugin, IConfigScope config)
- {
- string path = config.GetRequiredProperty("path", static p => p.GetString()!);
- InitPathAndLog(path, plugin.Log.CreateScope("widget-endpoint"));
-
- Enabled = config.GetValueOrDefault("enabled", static p => p.GetBoolean(), false);
- CorsAclHeaerDomains = config.GetValueOrDefault("cors-urls", static p => p.GetString(), null);
-
- bookmarks = plugin.GetOrCreateSingleton<BookmarkStore>();
- authManager = plugin.GetOrCreateSingleton<WidgetAuthManager>();
- }
+ private readonly BookmarkStore bookmarks = plugin.GetOrCreateSingleton<BookmarkStore>();
+ private readonly WidgetAuthManager authManager = plugin.GetOrCreateSingleton<WidgetAuthManager>();
+ private readonly bool Enabled = config.GetValueOrDefault("enabled", false);
+ private readonly string? CorsAclHeaerDomains = config.GetValueOrDefault<string?>("cors-urls", null);
protected override async ValueTask<VfReturnType> GetAsync(HttpEntity entity)
{
@@ -54,13 +52,13 @@ namespace SimpleBookmark.Endpoints
return VfReturnType.NotFound;
}
- /* if (!await authManager.IsTokenValidAsync(entity))
- {
- return VirtualClose(entity, HttpStatusCode.Unauthorized);
- }*/
+ /* if (!await authManager.IsTokenValidAsync(entity))
+ {
+ return VirtualClose(entity, HttpStatusCode.Unauthorized);
+ }*/
//Widgets might be loaded in an iframe, so we need to allow cross-site requests
- if(CorsAclHeaerDomains is not null && entity.Server.IsCrossSite())
+ if (CorsAclHeaerDomains is not null && entity.Server.IsCrossSite())
{
entity.Server.Headers.Append("Access-Control-Allow-Origin", CorsAclHeaerDomains);
}
@@ -79,7 +77,84 @@ namespace SimpleBookmark.Endpoints
entity.EventCancellation
);
- return VirtualCloseJson(entity, boomarks, HttpStatusCode.OK);
+ //Output memory buffer
+ VnMemoryStream output = new(32 * 1024, false);
+
+ try
+ {
+ string baseUrl = entity.Server.RequestUri.GetLeftPart(UriPartial.Authority);
+
+ CompileGlanceTemplate(baseUrl, output, boomarks);
+
+ //Assign glance template
+ entity.Server.Headers["Widget-Title"] = "Simple-Bookmark Widget";
+ entity.Server.Headers["Widget-Content-Type"] = "html";
+
+ return VirtualClose(entity, HttpStatusCode.OK, ContentType.Html, output);
+ }
+ catch(Exception ex)
+ {
+ output.Dispose();
+
+ Log.Error(ex, "Failed to complie glance template");
+ return VirtualClose(entity, HttpStatusCode.InternalServerError);
+ }
+ }
+
+ private static void CompileGlanceTemplate(string hostUrl, VnMemoryStream vms, BookmarkEntry[] bookmarks)
+ {
+ using IMemoryHandle<char> buffer = MemoryUtil.SafeAlloc<char>(32 * 1024, false);
+
+ ForwardOnlyWriter<char> writer = new(buffer.Span);
+
+ writer.Append("<!DOCTYPE html>");
+ writer.Append("<html>");
+ writer.Append("<head>");
+ writer.Append("<title>Simple-Bookmark Widget</title>");
+ writer.Append("</head>");
+ writer.Append("<body>");
+
+ writer.Append("<p class='size-h3'>");
+ writer.Append("<a class='bookmarks-link color-highlight size-h3' href='");
+ writer.Append(hostUrl);
+ writer.Append("'>Favorites:</a>");
+ writer.Append("</p>");
+ writer.Append("<hr class='margin-block-15'/>");
+
+ //Start bookmarks list
+ writer.Append("<ul class='list-horizontal-text'>");
+
+ foreach (BookmarkEntry entry in bookmarks)
+ {
+ writer.Append("<li>");
+
+ //Enable the built-in bookmarks link (makes it prettier)
+ writer.Append("<a class='bookmarks-link color-highlight size-h4' href='");
+ writer.Append(entry.Url);
+ writer.Append("'>");
+ writer.Append(entry.Name);
+
+ writer.Append("</a>");
+ writer.Append("</li>");
+ }
+
+ writer.Append("</ul>");
+
+ //Close the document
+ writer.Append("</body>");
+ writer.Append("</html>");
+
+ int byteCount = Encoding.UTF8.GetByteCount(writer.AsSpan());
+
+ using (UnsafeMemoryHandle<byte> utf8Buffer = MemoryUtil.UnsafeAllocNearestPage(byteCount, true))
+ {
+ //Encode utf8 bytes
+ Encoding.UTF8.GetBytes(writer.AsSpan(), utf8Buffer.Span);
+
+ vms.Write(utf8Buffer.AsSpan(0, byteCount));
+ }
+
+ vms.Seek(0, System.IO.SeekOrigin.Begin);
}
}
}