Inject tags to index.html

This commit is contained in:
Kieran 2023-12-22 13:27:07 +00:00
parent b9a9d7bd26
commit c019dcb3fb
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
8 changed files with 255 additions and 272 deletions

View File

@ -24,3 +24,5 @@ LICENSE
README.md
**/appsettings.*.json
**/data
**/build
**/dist

View File

@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
using AngleSharp;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using VoidCat.Model;
using VoidCat.Services.Abstractions;
@ -10,11 +10,13 @@ public class IndexController : Controller
{
private readonly IWebHostEnvironment _webHost;
private readonly IFileMetadataStore _fileMetadata;
private readonly VoidSettings _settings;
public IndexController(IFileMetadataStore fileMetadata, IWebHostEnvironment webHost)
public IndexController(IFileMetadataStore fileMetadata, IWebHostEnvironment webHost, VoidSettings settings)
{
_fileMetadata = fileMetadata;
_webHost = webHost;
_settings = settings;
}
/// <summary>
@ -28,22 +30,57 @@ public class IndexController : Controller
{
id.TryFromBase58Guid(out var gid);
var manifestPath = Path.Combine(_webHost.WebRootPath, "asset-manifest.json");
if (!System.IO.File.Exists(manifestPath)) return StatusCode(500);
// old format hash, return 404
if (id.Length == 40 && Regex.IsMatch(id, @"[0-9a-z]{40}"))
var ubDownload = new UriBuilder(_settings.SiteUrl)
{
Response.StatusCode = 404;
Path = $"/d/{gid.ToBase58()}"
};
var ubView = new UriBuilder(_settings.SiteUrl)
{
Path = $"/{gid.ToBase58()}"
};
var indexPath = Path.Combine(_webHost.WebRootPath, "index.html");
var indexContent = await System.IO.File.ReadAllTextAsync(indexPath);
var meta = (await _fileMetadata.Get(gid))?.ToMeta(false);
var tags = new List<KeyValuePair<string, string>>()
{
new("site_name", "void.cat"),
new("title", meta?.Name ?? ""),
new("description", meta?.Description ?? ""),
new("url", ubView.Uri.ToString()),
};
var mime = meta?.MimeType;
if (mime?.StartsWith("image/") ?? false)
{
tags.Add(new("type", "image"));
tags.Add(new("image", ubDownload.Uri.ToString()));
tags.Add(new("image:type", mime));
}
else if (mime?.StartsWith("video/") ?? false)
{
tags.Add(new("type", "video.other"));
tags.Add(new("image", ""));
tags.Add(new("video", ubDownload.Uri.ToString()));
tags.Add(new("video:url", ubDownload.Uri.ToString()));
tags.Add(new("video:secure_url", ubDownload.Uri.ToString()));
tags.Add(new("video:type", mime));
}
else if (mime?.StartsWith("audio/") ?? false)
{
tags.Add(new("type", "audio.other"));
tags.Add(new("audio", ubDownload.Uri.ToString()));
tags.Add(new("audio:type", mime));
}
else
{
tags.Add(new("type", "website"));
}
var jsonManifest = await System.IO.File.ReadAllTextAsync(manifestPath);
return View("~/Pages/Index.cshtml", new IndexModel
{
Id = gid,
Meta = (await _fileMetadata.Get(gid))?.ToMeta(false),
Manifest = JsonConvert.DeserializeObject<AssetManifest>(jsonManifest)!
});
var injectedHtml = await InjectTags(indexContent, tags);
return Content(injectedHtml?.ToHtml() ?? indexContent, "text/html");
}
public class IndexModel
@ -59,4 +96,41 @@ public class IndexController : Controller
public Dictionary<string, string> Files { get; init; }
public List<string> Entrypoints { get; init; }
}
private async Task<IDocument?> InjectTags(string html, List<KeyValuePair<string, string>> tags)
{
var config = Configuration.Default;
var context = BrowsingContext.New(config);
var doc = await context.OpenAsync(c => c.Content(html));
foreach (var tag in tags)
{
var ogTag = doc.CreateElement("meta");
ogTag.SetAttribute("property", $"og:{tag.Key}");
ogTag.SetAttribute("content", tag.Value);
doc.Head?.AppendChild(ogTag);
switch (tag.Key.ToLower())
{
case "title":
{
var titleTag = doc.Head?.QuerySelector("title");
if (titleTag != default)
{
titleTag.TextContent = tag.Value;
}
break;
}
case "description":
{
var descriptionTag = doc.Head?.QuerySelector("meta[name='description']");
descriptionTag?.SetAttribute("content", tag.Value);
break;
}
}
}
return doc;
}
}

View File

@ -1,85 +0,0 @@
@using VoidCat.Model
@model VoidCat.Controllers.IndexController.IndexModel
@inject VoidSettings Settings
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="apple-touch-icon" href="/logo.png"/>
<link rel="manifest" href="/manifest.json"/>
@if (Model.Meta != default)
{
var ubDownload = new UriBuilder(Settings.SiteUrl)
{
Path = $"/d/{Model.Id.ToBase58()}"
};
var ubView = new UriBuilder(Settings.SiteUrl)
{
Path = $"/{Model.Id.ToBase58()}"
};
<title>void.cat - @Model.Meta.Name</title>
<meta name="description" content="@Model.Meta.Description"/>
<meta property="og:site_name" content="void.cat"/>
<meta property="og:title" content="@Model.Meta.Name"/>
<meta property="og:description" content="@Model.Meta.Description"/>
<meta property="og:url" content="@ubView"/>
var mime = Model.Meta.MimeType;
if (!string.IsNullOrEmpty(mime))
{
if (mime.StartsWith("image/"))
{
<meta property="og:type" content="image"/>
<meta property="og:image" content="@ubDownload"/>
<meta property="og:image:type" content="@mime"/>
}
else if (mime.StartsWith("video/"))
{
<meta property="og:type" content="video.other"/>
<meta property="og:image" content=""/>
<meta property="og:video" content="@ubDownload"/>
<meta property="og:video:url" content="@ubDownload"/>
<meta property="og:video:secure_url" content="@ubDownload"/>
<meta property="og:video:type" content="@mime"/>
}
else if (mime.StartsWith("audio/"))
{
<meta property="og:type" content="audio.other"/>
<meta property="og:audio" content="@ubDownload"/>
<meta property="og:audio:type" content="@mime"/>
}
}
}
else
{
<title>void.cat</title>
<meta property="og:type" content="website"/>
<meta name="description" content="void.cat - free, simple file sharing."/>
}
@foreach (var ep in Model.Manifest.Entrypoints)
{
switch (System.IO.Path.GetExtension(ep))
{
case ".css":
{
<link rel="stylesheet" href="@ep"/>
break;
}
case ".js":
{
<script defer src="@ep"></script>
break;
}
}
}
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -13,6 +13,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.7" />
<PackageReference Include="AWSSDK.S3" Version="3.7.103.41" />
<PackageReference Include="BencodeNET" Version="5.0.0" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />

View File

@ -27,8 +27,8 @@ export class VoidApi {
readonly #uri: string;
readonly #auth?: AuthHandler;
constructor(uri: string, auth?: AuthHandler) {
this.#uri = uri;
constructor(uri?: string, auth?: AuthHandler) {
this.#uri = uri ?? "";
this.#auth = auth;
}

View File

@ -248,7 +248,7 @@ export function FilePreview() {
useEffect(() => {
if (info) {
const fileLink = info.metadata?.url ?? `${import.meta.env.VITE_API_HOST}/d/${info.id}`;
const fileLink = info.metadata?.url ?? `${import.meta.env.VITE_API_HOST ?? ""}/d/${info.id}`;
setFileSize(info.metadata?.size ?? 0);
const order = window.localStorage.getItem(`payment-${info.id}`);

View File

@ -20,8 +20,9 @@ export default defineConfig({
],
assetsInclude: [],
build: {
outDir: "build",
outDir: "build"
},
base: "/",
clearScreen: false,
resolve: {
alias: {

View File

@ -1,10 +0,0 @@
{
"files": {
"main.js": "/static/js/bundle.js",
"index.html": "/index.html",
"bundle.js.map": "/static/js/bundle.js.map",
},
"entrypoints": [
"static/js/bundle.js"
]
}